- Home
- LLBLGen Pro
- Architecture
CSLA-like remoting feature
Joined: 15-Oct-2004
I know very little about CLSA, not read the book only picked up snippets off the web. What I'm wondering is it only useful when you need remoting or is it a fully functioning business layer in its own right?
CSLA is a complete BL framework by its own right. CSLA's root go back to the COM days when Rocky published the framework for VB6. With .NET, CSLA matured to overcome all the limitations of its COM predecessor. IMHO CSLA goals are as follows:
1- Relieve the burden of writing "Plumbing Code" so that the developer would concentrate on programming the business requirements. This is achieved by structuring each business class so that it exposes public service methods and implements all the "Data Access" code in pre-defined code regions and in a pre-defined mannare. This in agreement with the manager classes that everyone is talking about here.
2- provide an easy way to scale the program from a single stand-alone machine to a distributed system spanning multiple application servers WITHOUT CHANGING CODE. This is achieved through the Data-Portal. Having a single point of entry for all your data access means you can coordinate the remoting of that single point of entry through code and the app's configuration file. All data access code uses the data portal for its work and the data-portal is smart enough to handle the situation through remoting depending on the configuration it reads from the App's config.
3- Table Role based security. CSLA offers the authentication part and also supports windows authentication.
JCL builds on the previous and extends in the following:
1- Data-Portal methods are not limited to the basic 4 CRUD methods. JCL's data-portal can execute any method passed to it from the business class
2- Security implementation has been extended to include Authorization. This is based on the concept of Screen's and Screen privilege's.
3- Include support for table based Auditing
4- include Error event logging to Text file or window’s event logs or the database (controlled through App's config file)
5- Use LLBL for Data Access instead of ADO.NET code. I don't have to preach about the benefits of using LLBL.. do I
6- use LLBL's support classes as base business classes in JCL (especially BusinessBase and BusinessCollectionBase). This truly made a world of a difference for both the business developer and the UI developer
7- use LLBL's filter-bucket approach with CSLA's criteria. This means that the Business developer can pass any complex filter expressions to the data portal in a very standard container that made the criteria class all that simple and yet very functional.
8- WinForms UI framework that is tightly integrated with JCL's business classes
I feel that JCL can do even more. Right now we are working with JCL to build an in-house ERP solution. We are hoping to add the following:
1- Data Access security. Example: If you have an invoices table but you don't want users for DEPARTMENT A to see\modify invoices of DEPARTMENT B.
2- beef-up the WinForms UI framework
3- ASP.NET UI Framework
We are hoping that JCL can add benefit to bot the LLBL and the CSLA communitis. For us it is a world of a difference for just using CSLA by itself. In addition, we are also hoping to tap ito the great minds here at LLBL's community to make JCL a better and more mature framework..
OMAR & BASHAR
Joined: 10-Oct-2004
Omar and Bashar thanks for the work you've put into this. There are things that seem interesting when I've read about CSLA, in terms of feature set. There are other parts that are gold plate for my typical needs, but I'm not highly concerned about optimizing that out of the equation just yet.
What I do wonder about is how much CSLA uses reflection. It looks like it uses reflection about any time it does mouch of anything across the dataportal. I can see how this provides flexibility and ease of implementation, I just wonder what your experience has been with CSLA and performance, and also what you did with JCL (and how it's performed for you).
This isn't a troll, just wondering what CSLA works like in reasonably demanding performance scenarios.
Joined: 15-Oct-2004
What I do wonder about is how much CSLA uses reflection. It looks like it uses reflection about any time it does mouch of anything across the dataportal. I can see how this provides flexibility and ease of implementation, I just wonder what your experience has been with CSLA and performance, and also what you did with JCL (and how it's performed for you).
The only place in JCL (and JCL) that uses reflection is the calls to the DataPortal methods. Rocky has justified this use best in his thread http://lhotka.net/Articles.aspx?id=4320e7f2-dd14-4928-b552-4eb0cb82cd68
Great work! Does this generate C# code or just VB.Net?
Both the JCL source code and the LLBL templates are in VB.NET. I don't think it will be a difficult excersice to convert the LLBL templates to C# but I prefered to leave this to a more vetren C# developer. It will be nice if some one in the LLBL community takes on the task of converting the templates and the JCL source code to C#.
I e-mailed Frans an update to JCL that contains some bug fixes that were missed when the zip file was prepared. I hope he gets the time to post it...
OMAR
Joined: 17-Aug-2004
I am working on a C# equivalent. I guess it would be ideal if we made the two codebases as similar as possible. Unfortunately, my implementation differs quite a bit. Mainly in that:
Its nowhere near as complete - no automatic UI generation. Cant see much utility in automatic generation of interface for anything other than grid controls. I think that using some extra metadata to drive generation of forms containing compositions of entities Kathleen Dollard style would be a more flexible soloution.
I use a companion class to store business rules and handlers, these are currently discovered by reflection when the BO is initialised. Didn't want to derive yet again to add rules and would like to keep the code generation process as flexible as possible. Merge region support in a future version of llblgen would be very nice.. ( At least until .NET 2.0 with its lovely partial class definitions makes code generation take off in a big way).
I would also like to integrate the activetracker change notification additions to CSLA.
I also can't really make any promisies about releasing what I have until I OK it with my client.
Ryan
Joined: 10-Oct-2004
omar wrote:
The only place in JCL (and JCL) that uses reflection is the calls to the DataPortal methods. Rocky has justified this use best in his thread http://lhotka.net/Articles.aspx?id=4320e7f2-dd14-4928-b552-4eb0cb82cd68
Yes, I've read that previously and founded it somewhat abbreviated. It explains his fairly subjective or anectdotal view of the performance implications (his assumptions about calls to the data layer, type of application, specifically web, and user dwell times are his own and not necessarily viable as extrapolated to an larger universe of applications without further examination).
I also didn't see a compelling reason why, just a justification for why not. If it works in practice, great/ I would just like to see more objective performance measures before making that kind of fundamental choice. If they doesn't exist, they don't exist (yet).
Joined: 02-Mar-2005
First off, thanks so much to Omar for his hard work on the CSLA/LLBLGen implementation, I've been playing with it a bit, and it looks like there is plenty of potential there.
I'm attempting to use the JCL framework in a forms test project, but I can't seem to figure out how to grab a collection of all entities. Because the BL layer hides the Adapter from me, I assume there is a method in the class to do this. The FetchList method requires a variable of some sort, and if I pass an empty value, or null, I get an exception.
Any insight would be greatly appreciated.
Joined: 12-Sep-2003
I've finally got some time to play with the JCL framework and even bought Rocky's book! Looks very promising but I'm having a few problems, could any one who's got the templates to work take a look a the following and let me know what I'm doing wrong?
I created a test DVD database and generated the DAL and business objects as per the PDF instructions, I used the following config and template:
Config: Name: JCL BusinessObject (and its Factory class) (Full / Not Safe) Vs.Net 2003 Version: 1.0.2004.1.072104 Vendor: Omar Shraim Description: Adapter. Generates code for the Business objects (and its Factory class). Creates 1 subclass per entity. Use this configuration to start your project as it generates all classes. Amount of tasks: 4
Template: Name: JCL Custom VB.NET template set for SqlServer (1.0.2004.1) Version: 1.0.2004.1.100604 Vendor: Solutions Design Target language: VB.NET
This produced 3 files in the _generate folder:
Dvd.vb Global.vb Lookup.vb
I then added them to a BL project and tried a compile, I got the following errors:
Global.vb
Global.vb(20): 'Conext' is not a member of 'JCL.BusinessIdentity'.
Line 20:
Select Case JCL.JCLCommon.JCLIdentity.Conext.UILanguage
Should be: ?
Select Case JCL.JCLCommon.JCLIdentity.Context.UILanguage
Fixed this simple problem but just thought I'd point it out for Omar and Bashar incase they wanted to update their template. Next...
Dvd.vb
Dvd.vb(38): 'Jcltest.BL.Dvd' must implement 'Overridable Function FieldToPredicate(field As SD.LLBLGen.Pro.ORMSupportClasses.EntityField2) As SD.LLBLGen.Pro.ORMSupportClasses.IPredicate' for interface 'JCL.IBusinessObject'.
The template generated a function in the "shared service" region:
Public Shared Function FieldToPredicate(ByVal field As EntityField2) As IPredicate
This function does not seem to satisfy the JCL.IBusinessObject interface which is declared as:
Public Overridable Function FieldToPredicate(ByVal field As SD.LLBLGen.Pro.ORMSupportClasses.EntityField2) As SD.LLBLGen.Pro.ORMSupportClasses.IPredicate
Could anyone give me a pointer to what I'm doing wrong? Or any idea what I need to add/change to get a successful compile?
I’m sure it’s something blindly obvious but I’ve been looking at it for so long I can’t see it. I usually program in C# so the change of language is not helping. I’m eventually hoping to convert the templates to C#, once I get them to compile and have a basic understanding of how they work of course!
Thanks
Joined: 17-Aug-2003
jshallard wrote:
Could someone point me to the referred to "remoting example" in the customer section - i can't seem to find it. Thanks
The remoting example was removed as it was very small (just two small classes, I'll paste them below) and the generated code was using selfservicing, not adapter.
CommonInterface.cs, which is in a separate assembly, called Interfaces. This assembly is then referenced by both client and server.
using System;
using Northwind.CollectionClasses;
namespace Interfaces
{
public interface IRemoteTest
{
CustomerCollection GetCustomers(string country);
}
}
Server. References generated code and interfaces (and ormsupport classes of course)
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;
using Interfaces;
using SD.LLBLGen.Pro.ORMSupportClasses;
using Northwind.CollectionClasses;
using Northwind;
using Northwind.FactoryClasses;
namespace Server
{
/// <summary>
/// Example manager for customers
/// </summary>
[Serializable]
public class CustomerManager: MarshalByRefObject, IRemoteTest
{
public CustomerManager()
{
}
public Northwind.CollectionClasses.CustomerCollection GetCustomers(string country)
{
CustomerCollection customers = new CustomerCollection();
IPredicate filter = PredicateFactory.CompareValue(CustomerFieldIndex.Country, ComparisonOperator.Equal, country);
customers.GetMulti(filter);
return customers;
}
}
}
web.config for server service (IIS controlled)
<configuration>
<appSettings>
<add key="Northwind.ConnectionString" value="data source=yourserver;initial catalog=yourcatalog;integrated security=SSPI;persist security info=False;packet size=4096" />
</appSettings>
<system.runtime.remoting>
<application>
<service>
<wellknown mode="Singleton" type="Server.CustomerManager, Server" objectUri="CustomerManager.soap" />
</service>
<channels>
<channel ref="http" />
<serverProviders>
<formatter ref="binary" />
</serverProviders>
</channels>
</application>
</system.runtime.remoting>
</configuration>
Client: references Interfaces, generated code and server (and ormsupportclasses of course)
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using Interfaces;
using Server;
using Northwind.CollectionClasses;
using SD.LLBLGen.Pro.ORMSupportClasses;
namespace Client
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class Startup
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
// register http channel
HttpChannel chan = new HttpChannel(0);
ChannelServices.RegisterChannel(chan);
try
{
MarshalByRefObject o = (MarshalByRefObject)RemotingServices.Connect(typeof(Interfaces.IRemoteTest),"http://yourserver/virtualdirforservice/CustomerManager.soap");
IRemoteTest server = o as IRemoteTest;
CustomerCollection customers = server.GetCustomers("France");
Console.WriteLine("Amount customers found: {0}", customers.Count);
Console.ReadLine();
}
catch(Exception ex)
{
Console.WriteLine("Ex: {0}", ex.Message);
Console.WriteLine("Stacktrace:\n{0}", ex.StackTrace);
}
}
}
}
You can also use this for the server, which doesn't use iis, but uses its own http listener: server
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using Interfaces;
using SD.NorthwindDAL.EntityClasses;
namespace Server
{
public class MyServer: MarshalByRefObject, IRemoteTest
{
public MyServer()
{
}
public CustomerEntity GetCustomer(string customerID)
{
Console.WriteLine("Customer request recieved for customer: {0}", customerID);
CustomerEntity c = new CustomerEntity(customerID);
return c;
}
}
/// <summary>
/// Summary description for Class1.
/// </summary>
public class ServerTest
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
HttpChannel chan = new HttpChannel(65100);
ChannelServices.RegisterChannel(chan);
Type serverType = Type.GetType("Server.MyServer");
RemotingConfiguration.RegisterWellKnownServiceType(serverType, "theEndPoint", WellKnownObjectMode.Singleton);
Console.WriteLine("Press Enter To Exit");
Console.ReadLine();
}
}
}
and a simple app.config:
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="Northwind.ConnectionString" value="data source=yourserver;initial catalog=yourcatalog;integrated security=SSPI;persist security info=False;packet size=4096"/>
</appSettings>
</configuration>
Then, on the client, do:
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using Interfaces;
using SD.NorthwindDAL.EntityClasses;
namespace RemotingTester
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class Startup
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
// register http channel
HttpChannel chan = new HttpChannel(0);
ChannelServices.RegisterChannel(chan);
MarshalByRefObject o = (MarshalByRefObject)RemotingServices.Connect(typeof(Interfaces.IRemoteTest),"http://servername:65100/theEndPoint");
IRemoteTest server = o as IRemoteTest;
CustomerEntity c = server.GetCustomer("CHOPS");
PrintCustomer(c);
Console.ReadLine();
}
public static void PrintCustomer(CustomerEntity enCustomer)
{
// print the customer object read
Console.WriteLine("-----[Customer]--------");
Console.WriteLine("CustomerID: {0}", enCustomer.CustomerID);
Console.WriteLine("Address: {0}", enCustomer.Address);
Console.WriteLine("City: {0}", enCustomer.City);
Console.WriteLine("CompanyName: {0}", enCustomer.CompanyName);
Console.WriteLine("ContactTitle: {0}", enCustomer.ContactTitle);
Console.WriteLine("Country: {0}", enCustomer.Country);
Console.WriteLine("Fax: {0}", enCustomer.Fax);
Console.WriteLine("PostalCode: {0}", enCustomer.PostalCode);
Console.WriteLine("Region: {0}", enCustomer.Region);
Console.WriteLine("\n");
}
}
}
and interfaces is slightly changed, as it now returns a single customer:
using System;
using SD.NorthwindDAL.EntityClasses;
namespace Interfaces
{
public interface IRemoteTest
{
CustomerEntity GetCustomer(string customerID);
}
}