CSLA "Correct" Way

CSLA "Correct" Way

Old forum URL: forums.lhotka.net/forums/t/5339.aspx


Wbmstrmjb posted on Wednesday, September 03, 2008

We are struggling to understand the "proper" way to implement CSLA objects in general for any typical database application. Going the ProjectTracker route of each object being whatever properties that object needs from various sources is nice, but does not lend itself to codegen. Going the route of one object per table and then combining those objects in larger objects doesn't have great performance because each object needs to make it's own database calls.

So my question is as follows. What is the normal proper CSLA way to implement the following (dumb but easy to follow) example:

Classes: Person (PersonID, Name), Car (CarID, PersonID, Make), MaintenaceRecord (MaintenanceID, CarID, StationID, ServiceDone), Station (StationID, StationName)

Relationships: Person has 0 or more cars. Cars have 0 or more MaintenaceRecords. MaintenanceRecords are for a single shop.

In a UI that shows the Person Info and Car Info with MaintenaceRecords with Station Info below, you need all 4 tables of data.

Option 1: The non-codegen way to do this would be to create a task-centric object that had the properties of the Customer and Car in it and give it a child of MaintenanceRecords collection and each MaintenanceRecord would also include the Station Info in it properties. Basically 3 objects needed, a RO PersonCar, a RO StationMaintenanceRecord, and a ROList StationMainenanceRecordList. These are all custom objects that do not work well with codegen (unless I am missing something with codegen). The SQL used to get this info needs to be custom SPs or custom join queries within the objects.

Option 2: To codegen it, we codgen all 4 tables into objects and lists for each. Now we have 8 total objects. We make a new BO that contains an instance of Station and an instance of MaintenanceRecord and call it StationMaintenanceRecord (like above). Then make a list of those StationMaintenanceRecordList (like above). We make a new BO that contains an instance of Person, and an instance of Car, and an instance of StationMaintenanceRecordList and call it PersonCar (like above). Each of the 8 lower level objects were codegened, but now a load of PersonCar take 4 seperate calls to the DB instead of one call (or maybe 2 in the case of not passing a multi-return DR down to the child).

Both options have their problems and both make certain things easier. Obviously this scenario is very basic and so codegen isn't necessary, but I am asking in the theoretical sense so that our rather large application can use whatever methodology is presented. Are we missing a third option completely that would be the best practice? Of the two I described, neither seem feasible because Rocky advocates codegen, but also would not advocate a mash up of objects that each make independent calls. So where are we going wrong?

Thanks,
Mike

sergeyb replied on Wednesday, September 03, 2008

Mike,

You might want to look at the DeepData sample. It shows how to load data into a graph from a single stored procedure. If you want to code gen, sure you can do that. IMHO, I would code gen all the objects you listed in Option 2, then manually modify code to create a single object graph that has customer as the root. All you would need to do is add a property for each child or child list and ChildPortal access methods in your children. So, you have the best of both worlds - you code gen a bulk of code, but you modify it to be correct from business perspective. I would definitely use a single SP with multiple results sets as well.

Does this make sense?

Sergey Barskiy
Principal Consultant
office: 678.405.0687 | mobile: 404.388.1899
Magenic ®
Microsoft Worldwide Partner of the Year | Custom Development Solutions, Technical Innovation

-----Original Message-----
From: Wbmstrmjb [mailto:cslanet@lhotka.net]
Sent: Wednesday, September 03, 2008 8:39 PM
To: Sergey Barskiy
Subject: [CSLA .NET] CSLA "Correct" Way

We are struggling to understand the "proper" way to implement CSLA objects in general for any typical database application. Going the ProjectTracker route of each object being whatever properties that object needs from various sources is nice, but does not lend itself to codegen. Going the route of one object per table and then combining those objects in larger objects doesn't have great performance because each object needs to make it's own database calls.

So my question is as follows. What is the normal proper CSLA way to implement the following (dumb but easy to follow) example:

Classes: Person (PersonID, Name), Car (CarID, PersonID, Make), MaintenaceRecord (MaintenanceID, CarID, StationID, ServiceDone), Station (StationID, StationName)

Relationships: Person has 0 or more cars. Cars have 0 or more MaintenaceRecords. MaintenanceRecords are for a single shop.

In a UI that shows the Person Info and Car Info with MaintenaceRecords with Station Info below, you need all 4 tables of data.

Option 1: The non-codegen way to do this would be to create a task-centric object that had the properties of the Customer and Car in it and give it a child of MaintenanceRecords collection and each MaintenanceRecord would also include the Station Info in it properties. Basically 3 objects needed, a RO PersonCar, a RO StationMaintenanceRecord, and a ROList StationMainenanceRecordList. These are all custom objects that do not work well with codegen (unless I am missing something with codegen). The SQL used to get this info needs to be custom SPs or custom join queries within the objects.

Option 2: To codegen it, we codgen all 4 tables into objects and lists for each. Now we have 8 total objects. We make a new BO that contains an instance of Station and an instance of MaintenanceRecord and call it StationMaintenanceRecord (like above). Then make a list of those StationMaintenanceRecordList (like above). We make a new BO that contains an instance of Person, and an instance of Car, and an instance of StationMaintenanceRecordList and call it PersonCar (like above). Each of the 8 lower level objects were codegened, but now a load of PersonCar take 4 seperate calls to the DB instead of one call (or maybe 2 in the case of not passing a multi-return DR down to the child).

Both options have their problems and both make certain things easier. Obviously this scenario is very basic and so codegen isn't necessary, but I am asking in the theoretical sense so that our rather large application can use whatever methodology is presented. Are we missing a third option completely that would be the best practice? Of the two I described, neither seem feasible because Rocky advocates codegen, but also would not advocate a mash up of objects that each make independent calls. So where are we going wrong?

Thanks,
Mike

rsbaker0 replied on Wednesday, September 03, 2008

Wbmstrmjb:
...Going the route of one object per table and then combining those objects in larger objects doesn't have great performance because each object needs to make it's own database calls....

I too am interested in this discussion, but this is the route we have taken and so far it is performing much better than the implementation it is replacing. I have concerns though, and we have made extensive use of "lazy loading" to avoid loading child objects unless they will actually be used.

From my experience in this forum, there seems to be strong support for tailoring the object design to match the exact way they are used (e.g. the "use case"), with it being fine -- even preferable from a performance standpoint -- to have multiple objects mapped to the same database table. However, I don't see how that lends itself to code generation unless there is some intermediate design tool I'm not familiar with.

tmg4340 replied on Wednesday, September 03, 2008

This has been discussed a couple of times in other threads.

Code generation cannot necessarily develop all your classes.  However, it can often be used for generating the majority of your classes - those maintenance-type screens where the use case pretty much matches the database table structure.  Code generation helps get these more mundane screens out of the way, leaving you time to work on the more interesting sections.

Code generation can also help for the more interesting sections as well.  Often times, code generation can develop a majority of the class structure.  It won't be a 100% solution, but depending on how closely the use case matches the table structure, it can get you a long ways towards 100% - and it's boilerplate code you didn't have to write.  Smile [:)]

HTH

- Scott

Q Johnson replied on Wednesday, September 03, 2008

Wbmstrmjb:
...However, I don't see how that lends itself to code generation unless there is some intermediate design tool I'm not familiar with.

Maybe that "intermediate design tool" you mention here is just a code gen template waiting to be built.

I just want to offer for your consideration that the degree to which a solution's architecture "lends itself to code generation" is probably as dependent on how much YOU lend yourself to code generation as it is on other factors.

Using templates produced by others (ala CSLAGEN or other template providers of other code generation products) to generate the CSLA classes is a great start.  It certainly makes you more productive than if you wrote them by hand.  But there are other opportunities for applying it that are limited only by your imagination and willingness to write templates. 

Give some thought to letting it do even more for you.

Good luck!

 

dlambert replied on Wednesday, September 03, 2008

I've had pretty good luck leaving objects like Customer and Car in their own objects, but filling multiple objects out of one SQL call.  I haven't looked at the Deep Data sample, but this sounds very much like the approach that's taken there.

I'd create a composite object like you mentioned in cases where I want to load just a portion of a deep / wide object graph in a large collection.  In a case like this, it's worth creating the custom task-centric object to more closely match the needs of the UI, because you really cut the data access needs vs. loading the whole object graph for multiple objects.

Wbmstrmjb replied on Thursday, September 04, 2008

I think this is making more sense. So I can leave each table in it's own class. But instead of puting the SQL into each individual class, build a combined SP that the parent loads and distributes. That should work while still allowing for the classes (less the data access) to be codegened.

One other question I have is what are the pros and cons to inheritance vs. wrapping the objects? In the case of the PersonCar in my example, would it be better to extend Person and include an instance of Car as a property, or make a PersonCar object that has an instance of each? Is there a strong reason to do one or the other?

dlambert replied on Thursday, September 04, 2008

Well, if I understand the scenario, the relationship between Person and Car is pretty central to your model.  I'd go ahead and just have a Person object with a Cars list that's going to have zero or more Car objects in it.

If you need to compress this relationship, like for searching or lists or something, then follow the Project Tracker example and do a PersonInfo readonly object, or whatever you need for your scenario.

rsbaker0 replied on Friday, September 05, 2008

Speaking of inheritance...

This is something of a side-issue, but you'll also get great flexibility by not deriving directly from the CSLA data object classes themselves but instead inserting a common base class between your generated classes and the intended CSLA base class. (specifically, your own BusinessBase, BusinessListBase, EditableRootListBase, ReadOnlyListBase, and CommandBase -derived common root classes)

dlambert replied on Friday, September 05, 2008

Good point.  I discovered this particular tip a little too late to conveniently implement in my first CSLA project, but I think it would have helped in a couple spots.

Wbmstrmjb replied on Friday, September 05, 2008

Can you shed some light as to common functionality you would add to the middle layer?

rsbaker0 replied on Saturday, September 06, 2008

^^^^^

Ours is a special case, since we are using an Object-Relational mapper for data access.

Our "middle layer" does all the data access -- using the ORM (the Wilson ORMapper in our case, but NHibernate also can be made to work).

However, here are some other things our middle layer does:

(1) Referential integrity constraint enforcement - blocks deletion where not allowed, propagates deletes where allowed, and provides for Validation rules to warn user when foreign key value doesn't exist, or when new primary key value collides with an existing object.

(2) COUNT, EXISTS, and aggregate functions (SUM, MIN, MAX) via CommandBase for all of our table-mapped objects.

(3) Logging

(4) Auditing

(5) Retrieval of database schema information so we can enforce maximum field length (again via business rules) to match the actual back-end database size.

(and this is just a sampler...)

We have to support 3 different back-end databases (various versions of Oracle, SQL Server, and Access), so this approach might not work for everyone, but it's working very well for us so far.

Anyway, this is just to illustrate what you can do in a middle layer...

 

Copyright (c) Marimer LLC