Using partial classes with code generator...best practice?

Using partial classes with code generator...best practice?

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


GlennA posted on Friday, February 01, 2008

Hi all,

Been doing some prototyping with CSLA for the last couple of weeks with pretty good success.  Now branching a bit more into code generation where applicable to eliminate some of the coding time.  I've been using CodeSmith and the associated CSLA templates.

I'm looking for the best way to seperate the portion of the class that is generated from the part that is likely to be customized, the thought being that in many cases one can regenerate the class without losing customizations.  I know that in some cases the domain object might have to be more hand-crafted if it doesn't jive well with a table, but I'm talking about cases where the domain object is reasonably close to the table structure (which is where a code generator would be useful anyhow).

I'm guessing the parts that are most likely to be customized and therefore split into their own class would be:

* Addition of properties, like an aggregate not stored directly in the database or some string catting (FirstName + LastName = FullName).

* Additional validation / business rules, both 'Common' and 'Custom'.

* Authorization Rules.

Before actually trying it, I was considering modifying the codegen methods to create a call to 'extension' methods in a partial class that would provide for additions to what is generated.  Not sure if this would die because of the way reflection is used, but seems like it might work.  The custom class would have to implement an interface to make sure these methods actually exist. 

It seems like others have expressed similar desires, but I was unable to find the answer I was looking for from searching the forum.  The codegen looks to be a big timesaver, but would like to be able to run it more than once without worry of losing customizations.

Any methods of doing this that someone would like to share would be greatly appreciated.  Or if this has been discussed before (I'm new here), kindly point me to the thread.

Regards,

Glenn

tmg4340 replied on Friday, February 01, 2008

There are generally two methods of handling this issue: partial classes and inheritance.

I haven't looked at CodeSmith in a while, but most of the current code generators seem to favor partial classes in their design.  Usually, the reason supplied is that it keeps the object hierarchy smaller, especially when you consider that the inheritance solution strongly suggests that you create subclasses of every code-genned class for consistency.  If you plan on adding custom code to most of the genned classes, that's fine.  But if a lot of them are usable "as-is", then you've potentially created a lot of subclasses for nothing.

The advantage to using the inheritance option is that CSLA was built for inheritance, so you can always override methods in your code-genned classes for additional validation/authorization/etc.  Partial classes can override base-class members, but you can't override the same method in both files, and I don't think you can override methods from one partial class file in another either.

(You might be able to do this using VS2008 and partial methods, but I haven't delved into that yet, so I can't say for sure.)

Of course, there's a strong recommendation that you should create subclasses of the CSLA base classes that you use on your own anyway - something like this:

BusinessBase -> YourBusinessBase -> YourBusinessObject

This is suggested so that you have a place to put business-specific shared code, but it also helps with this issue as well, because you can mix both ideas.  You build in your extension method hooks into YourBusinessBase, code-gen YourBusinessObject (to inherit from YourBusinessBase) to handle the basics, and use a partial class to override the extension methods.

I realize this flies somewhat in the face of the argument against inheritance I made above.  I'm not saying inheritance is bad - but you have to know how best to use it.  IMO, you are much more likely to modify the CSLA-based subclasses to include standard functionality for your business than add custom code to every business object.  Those subclasses also provide you with a level of insulation against framework changes - doing it the other way means your code-genned objects all inherit directly from a CSLA object, so a change there ripples through the entire object space.

HTH

- Scott

JoeFallon1 replied on Monday, February 04, 2008

Scott presents a lot of excellent points. I recommend most of them. I happen to fall into the inheritance camp though. I have been using CSLA for 4 years and partial classes were not an option when I first started. As Scott mentioned, partial classes have their own issues to overcome so I never bothered to try and switch to that paradigm.

I have a Base class which inherits from CSLA and then all my BOs inherit from that. This is a highly recommended practice.

Then I codegen the BO which inherits from my Base class. Then I make an empty final type which inherits from the code gen class. What I have learned is that the final type does NOT stay empty very long. In fact I don't think I have a single class which is still empty. You quickly learn that you need other fields from other tables or you have to override methods to fetch the correct data.

I have previously posted that I turned DataPortal_Fetch into a main method which then calls many overridable sub methods.

e.g.
FetchData(criteria, cn)
PostFetchData(criteria, cn)
FetchChildren(criteria, cn)

This way when you need to fetch an extra field you can override FetchData and call MyBase.Fetch to do your "standard" work and then grab your one extra field.

The same applies to Validation Rules. Override it and call MyBase to get the "standard" rules. Tip: when code generating rules be sure that they always apply to the type and its subclasses because you cannot override them. So I have lots of common rules which are always part of a String type (Max Length, ValidCharacters, etc.) But I hand write a lot of rules in the next level.

Joe

 

GlennA replied on Monday, February 04, 2008

Thanks Joe and Scott...valuable feedback.

I guess time will tell whether using partial classes or inheritance is the way to go.  If I find myself most often wanting to add behavior to the autogen methods, I'll use inheritance, and if I'm looking to add methods to the class, partial may be the way to go.

Think we'll end up using inheritance based on what we've seen in our initial prototyping.  So you'd modify the code generator to create an abstract BO class with virtual methods, and then add to the instantiable subclass as needed?

Joe, what do you find yourself putting into the base class that inherits from CSLA and is the BO parent?  Hadn't thought of doing that, wondering what would be put in there.

Thanks again fellas!

Glenn

david.wendelken replied on Monday, February 04, 2008

I really think the question: "Should I use partial classes or inheritance?" is the wrong question, and therefore will always result in a wrong answer!

The correct questions are more like: "When should I use partial classes and when should I use inheritance?  What are the guidelines for deciding?"

To me, inheritance is great when I most sub-classes will do the same thing - and when overriding that behavior is easy.

I use partial classes when I generate my business objects.  I use C#, so if my business object's "root name" was employee, I generate the following class files:

If I need to do customization, I create a new file.  For example, let's say I wanted to add a new, computed property, "EmployeeAge", which compares the BirthDate property with the system date/time.  So, I would create EmployeeInfo.cs and Employee.cs and add the EmployeeAge property to both of them.

If I re-run my generators, I will never lose the custom code I just added, because they mess with the .generated.cs files.

Let's say that I want to add a read-only property, DepartmentName, to EmployeeInfo.  However, in this case, I want to load the DepartmentName property from the Department table that goes with the employee's DepartmentId value when the EmployeeInfo object is initialized.

I'll need an EmployeeInfo.cs and an EmployeeInfoList.cs file for this.

EmployeeInfo.cs gets the read-only DepartmentName property along with its corresponding private variable.  I then cut the internal constructor that accepts the SafeDataReader argument out of EmployeeInfo.generated.cs and paste it into EmployeeInfo.cs.

I use the same process for the EmployeeInfoList DataPortal_Fetch method because it needs to be modified to include the department name in the query.

If I re-run my generators, the compiler will complain because there will be duplicate constructor and DataPortalFetch methods.  I will never lose code by having it overwritten by the generator.  I will have to remove the duplicate methods from the .generated.cs files.  If I don't know exactly what changes were made, I may want to reconcile both method versions to make sure I include other new properties that might be due to new columns in the business object's underlying main data table or remove obsolete ones.

This has been a safe and simple method for me so far.  I'm sure there's a better way, and if someone explains it, I might even use it. :)

 

 

ajj3085 replied on Monday, February 04, 2008

I agree; partial classes were created to facilite code-gen,  inhertience has nothing to do with code generation though and is a design decision.  You inherit to get behavior, not to use code gen..

Use partial classes if you're code genning; usually you're generating the properties and maybe fields and constructors.  The rest (the business rules, overidding AddBusinessRules, etc) should probably be in the file that's not generated.

JoeFallon1 replied on Monday, February 04, 2008

GlennA:

#1. So you'd modify the code generator to create an abstract BO class with virtual methods, and then add to the instantiable subclass as needed?

#2. Joe, what do you find yourself putting into the base class that inherits from CSLA and is the BO parent?  Hadn't thought of doing that, wondering what would be put in there.

#1. Yes. Exactly.

#2. I have tons of great stuff in my Base class which inherits from CSLA. Rocky just built some things in 3.5 which may cause me to remove some of them though.

a. I have Transaction code to Begin, End and Rollback transactions. The root BO which starts the tr puts it in the LocalContext and then other BOs will use the existing tr in LocalContext rather than create their own. This allows the BOs to be coded the same way and the only one that commits the tr is the one that started it. The others call commit but nothing happens unless they started it. There is also clean up code to flush the tr from LocalContext.

b. I override Save (3 times - giving me lots of flexibility in what gets checked or not)

c. I override IsDirty and IsValid. I use code to see which BOs and collections are contained by the root BO and then base class takes care of checking them when IsValid is called on the root. I no longer have to do it for each BO I create.

d. I have a GetAllBrokenRulesString function which returns the list of rules.

e. I implement my own Interface which inherits from IEditableBusinessObject. This allows me to treat each BO polymorphically by casting to the interface. It allows me to use some slick code in lots of tight spots. I added 9 methods and properties to it.

My BusinessListBase has its own set of neat code which I pulled out of each collection and moved here. I was code generating a lot of this stuff but it is not needed if it is in tthe Base class.

e.g. I have Add and Remove methods which optionally Test for Existence first. I have an Interface so I have access to common properties of my collections (Count, IsDirty, etc.)

Joe

 

 

 

 

Copyright (c) Marimer LLC