One of the classic "problems" with code generation is the situation where you need to add custom code to generated code. For example, it is very easy to generate simple ("anemic") domain objects from a database schema, but what about when you get to the point when you want to add behaviors to those objects? Where do you put that code? If you place it in the file that was generated, you will lose the handcrafted code unless you either never regenerate that file (very unlikely) or manually (or programmatically) remove and then re-insert the code; with the exception of programmatically preserving handcrafted code, you don't want to bother with those options.
Up until recently, the way you typically get around this is to use an inheritance model. You put all of your generated code in a base class and all of the custom code in a derived class. This allows you to regenerate the base class any time you would like without fear of losing handcrafted code. Of course, some would say there are cons to this approach, such as namespace pollution, issues accessing private base class members, etc. - I personally haven't had any problems with the inheritance model, but maybe I just don't write code that is interesting enough to suffer from these issues. :P
So, where do partial classes come into play? Well, they were, at first (by those who didn't think hard about actual implementation), hailed as a solution to the problem alluded-to previously. Instead of using contrived inheritance, we could now have one type spread across two or more files. Yippee! Now that we can have a generated file and a custom, handcrafted file that define the same type, all of our code gen related problems would be solved! But not so fast. While it does provide a great construct for some code gen situations, it is not necessarily the ideal in all cases (which is to be expected, nothing is a silver bullet) - of course, I am examining one simple situation here, it doesn't mean partial classes will not work well for other situations. Let's use an example that came up recently in my own work. In the past, I would generate code and use the following construct for the beginnings of my domain objects (i.e. before I added the handcrafted behavioral code to the domain object):
Base class:
public abstract class UserBase
{
private string m_FirstName = string.Empty;
private string m_LastName = string.Empty;
private int m_Age = 0;
public string FirstName
{
get { return m_FirstName; }
set { ValidateFirstName(value); m_FirstName = value; }
}
public string LastName
{
get { return m_LastName; }
set { ValidateLastName(value); m_LastName = value; }
}
public int Age
{
get { return m_Age; }
set { ValidateAge(value); m_Age = value; }
}
public UserBase() { }
public UserBase(string firstName, string lastName, int age)
{
FirstName = firstName;
LastName = lastName;
Age = age;
}
protected virtual void ValidateFirstName(string firstName) { }
protected virtual void ValidateLastName(string lastName) { }
protected virtual void ValidateAge(int age) { }
}
Derived class:
public class User : UserBase
{
public User() : base() { }
public User(string firstName, string lastName, int age) : base(firstName, lastName, age) { }
protected override void ValidateFirstName(string firstName)
{
if (firstName.ToUpper() == "FOOBAR")
{
throw new InvalidDataException(string.Format("Your first name cannot possibly be '{0}'!!!", firstName));
}
}
}
The thing I want to point out is that in the property setters in the base class, we call some virtual methods to validate data. As you can see, those methods have no implementation, so if we derive a class from UserBase, and don't override those methods, no data validation happens. That is just fine, because maybe our code gen process doesn't provide for generating that type of validation (it could if we had a more robust code gen process, but let's simply examine this simple case). So, now we are free to override the virtual methods in the derived class in order to provide data validation (as illustrated by the overridden ValidateFirstName method).
Well, guess what? This is not so easy using partial classes. If I have the following generated partial class, how do I duplicate the same behavior as illustrated above by the inheritance model?
Generated partial:
public partial class User
{
private string m_FirstName = string.Empty;
private string m_LastName = string.Empty;
private int m_Age = 0;
public string FirstName
{
get { return m_FirstName; }
set { ValidateFirstName(value); m_FirstName = value; }
}
public string LastName
{
get { return m_LastName; }
set { ValidateLastName(value); m_LastName = value; }
}
public int Age
{
get { return m_Age; }
set { ValidateAge(value); m_Age = value; }
}
public User() { }
public User(string firstName, string lastName, int age)
{
FirstName = firstName;
LastName = lastName;
Age = age;
}
protected void ValidateFirstName(string firstName) { }
protected void ValidateLastName(string lastName) { }
protected void ValidateAge(int age) { }
}
Custom partial:
public partial class User
{
// how do I take care of writing custom data validation within this file?!
}
I was in this situation today. We (coworkers and myself) decided a few weeks ago, without giving it a terribly good deal of thought, that we would go the partial class route on our generation of domain objects and such. But now I needed to validate data on the setter of one of the properties of the class. How was I going to do that? I came up with a completely horrible hack involving delegates which I decided was out of the question. I may not write the most elegant code, but I want the code to be somewhat readable - having a delegate for each property's setter was NOT in keeping with that idea. I am either going to go back to using the inheritance model or will start using the PreserveRegions functionality of CodeSmith to preserve my handcrafted code - partial classes have not been as useful as I once hoped they would be. Oh well!