devxlogo

Using CodeDOM Code Generation to Implement Repetitive Patterns

Using CodeDOM Code Generation to Implement Repetitive Patterns

attern gurus are quick to remind us that patterns are not code. Patterns exist at a level above the code. Or, as Martin Fowler says (PDF), “Patterns provide a mechanism for rendering design advice in a reference format.” This implies that patterns are too abstract to be implemented in a generic fashion using object-oriented techniques. But there is a set of low-level patterns (some might even call these idioms) that you can implement in a reusable fashion?not with object-oriented techniques, but using code generation. In this article, I’ll introduce you to just such a pattern, and then show you how to build a reusable implementation with .NET’s CodeDOM namespace.

Part 1: Introducing the Property Union
The PropertyUnion pattern is very simple: It’s a wrapper class that exposes all the relevant properties of a given inheritance hierarchy by flattening it so that client code doesn’t have to do any type-checking or casting before accessing the properties of an object.

If that explanation makes no sense, that’s alright; keep reading. If, on the other hand, you’ve used this pattern, or a similar one, then you can skip ahead to Part 2 and get started with the code generation.

Motivation

?
Figure 1. A Simple Domain Model: Employees need to store extra information beyond the Contact class, so the domain consists of two related classes.

Object-oriented programming languages and databases can take very different approaches to modeling the same problem domain. Someone coined the phrase “impedance mismatch” to describe the difference in philosophy, and nowhere is the mismatch more evident than mapping inheritance hierarchies onto database tables. Since traditional relational databases don’t support inheritance, you have to decide how to map your inheritance hierarchy onto the flat relational model.

Here’s an example. Let’s say you’re building a simple contact management application. You want to work with plain old contacts as well as contact information for employees which need extra, employee-specific data. One solution is to create an inheritance hierarchy with two classes: Contact (a concrete base class) and its subclass Employee (see Figure 1).

Now for the data model. The simplest solution is what the gurus call “single table inheritance.” The idea is to create a single table for the entire hierarchy, with a set of fields that represents a union of all fields in the hierarchy, and a “type discriminator” field (either a string or an integer associated with an enum) to let users know which type each row represents (see Figure 2).

Table 1 shows a representative set of rows from the sample data. Notice how the shaded fields are applicable only to contacts of type Employee.

?
Figure 2. An Even Simpler Data Model: In the database, you want to have only one table, but when you flatten the hierarchy in this way, not every field is applicable to every record.

Table 1. The table shows the intersection of properties from the Contacts and Employees tables. The unshaded columns apply to all contacts, while the shaded columns apply only to contacts of type Employee.

ContactTypeFamilyNameGivenNameBirthDateTitleSalaryMailStop
ContactBloggsJoe1977.07.03???
ContactPilgrimBilly1924.02.12???
EmployeeHoldenJudge1822.01.01President1012d
EmployeeSlothrupTyrone1922.03.12Peon344f

This approach has a few advantages over a data-model which uses multiple tables:

  • Simplicity. Reports are easier to write against a single table.
  • Performance. A single table means that the data-layer only has to hit the database once for every type of employee.
  • Maintainability. With a single table, you can refactor properties into different levels of your object model without affecting your data model.

Now, it’s time to write the data-layer. The most obvious way to create objects based on rows from the database is to switch on the type discriminator. The following data-layer method builds a Contact or an Employee object from a strongly typed datarow.

   private static Contact BuildContact(      Contacts.ContactRow contactRow)   {      Contact contact = null;               switch(contactRow.ContactType)      {         case 0:            contact = new Contact();            break;         case 1:            Employee asEmployee = new Employee();            asEmployee.Salary = contactRow.Salary;            asEmployee.EmployeeNumber =                contactRow.EmployeeNumber;            asEmployee.MailStop = contactRow.MailStop;            asEmployee.Title = (Employee.JobTitle)                contactRow.Title ;            contact = asEmployee;            break;         default:            throw new Exception("Unknown ContactType");      }      contact.BirthDate=contactRow.BirthDate;      contact.FamilyName=contactRow.FamilyName;      contact.GivenName=contactRow.GivenName;         return contact;   }

That’s not too pretty. And it’s na?ve, because the problem gets far worse when it’s time to build the user interface, as you can see in the following UI-layer code, which fills out various UI widgets?again, based on the type of the contact object being edited.

   private void EditContact(Contact contact)   {      Employee employee = contact as Employee;      bool isEmployee = employee != null;         this.tbEmployeeNo.Enabled = isEmployee;      this.tbMailStop.Enabled = isEmployee;      this.nudSalary.Enabled = isEmployee;      this.cbTitle.Enabled = isEmployee;         this.tbGivenName.Text=contact.GivenName;      this.tbFamilyName.Text=contact.FamilyName;      this.dtpBirthDate.Value = contact.BirthDate;         if(isEmployee)      {         this.tbEmployeeNo.Text =             employee.EmployeeNumber.ToString();         this.tbMailStop.Text = employee.MailStop;         this.cbTitle.SelectedItem = employee.Title;         this.nudSalary.Value=employee.Salary;      }      return;   }

The duplicated logic in the methods shown above should trigger some warning bells. In fact, you could end up writing the same code as many as four times because you’re creating and modifying Employee objects and Contact objects in both the data layer and in the UI.

Implementing the Property Union Pattern
So what do you do with duplicated logic? You encapsulate it. The PropertyUnion pattern moves all type-checking code into a single class, flattening out an inheritance hierarchy. Client code can then treat all types in the hierarchy uniformly.

In just a minute, I’ll show you how the implementation works?if you haven’t already guessed?but for now, let’s look at the client code. Here’s a much simpler data-layer method that’s functionally identical, yet doesn’t implement (or duplicate) any type-checking:

   private static Contact BuildContact(      Contacts.ContactRow contactRow)   {      ContactPropertyUnion.TypeEnum type =          (ContactPropertyUnion.TypeEnum)         contactRow.ContactType;      ContactPropertyUnion union = new          ContactPropertyUnion(type);         union.Salary = contactRow.Salary;      union.EmployeeNumber = contactRow.EmployeeNumber;      union.MailStop = contactRow.MailStop;      union.Title = (Employee.JobTitle)          contactRow.Title ;      union.BirthDate=contactRow.BirthDate;      union.FamilyName=contactRow.FamilyName;      union.GivenName=contactRow.GivenName;         return union.Wrapped;   }

You can see that the code above uses a PropertyUnion class instance to set the data. Now, here’s a version of the UI method that also relies on a PropertyUnion class to enable widgets, and populate them with appropriate data?again, without type checking.

   private void EditContact(Contact contact)   {      ContactPropertyUnion wrapper = new          ContactPropertyUnion(contact);         this.tbEmployeeNo.Enabled =          wrapper.HasEmployeeNumberGetter;      this.tbMailStop.Enabled =          wrapper.HasMailStopGetter;      this.nudSalary.Enabled = wrapper.HasSalaryGetter;      this.cbTitle.Enabled = wrapper.HasTitleGetter;         this.tbGivenName.Text=wrapper.GivenName;      this.tbFamilyName.Text=wrapper.FamilyName;      this.dtpBirthDate.Value = wrapper.BirthDate;      this.tbEmployeeNo.Text =          wrapper.EmployeeNumber.ToString();      this.tbMailStop.Text = wrapper.MailStop;      this.cbTitle.SelectedItem = wrapper.Title;       this.nudSalary.Value=wrapper.Salary;         return;   }

Conceptually, the PropertyUnion pattern is very similar to the object-oriented bedrock concept of polymorphism. Both techniques let client code ignore differences between related objects, but the two differ in the amount of information they make available. Polymorphism exposes the intersection of all available properties?that is, the set of members that all related objects have in common, the base type. PropertyUnion exposes, well, the union of all properties in an inheritance tree.

The ContactPropertyUnion Class
Here’s another (admittedly informal) bedrock principle of object oriented design: If you have to do something ugly?and type-checking certainly qualifies?then at least put all that ugliness into a single place. The bad news is that the need to write type-checking code doesn’t go away when you use the PropertyUnion pattern, but at least it’s centralized. For this sample application, the ContactPropertyUnion class centralizes the type-checking code.

?
Figure 3. A New and Improved Object-Model: Each instance of the ContactPropertyUnion class contains an instance of type Contact.

This pattern is simply a wrapper class, called ContactPropertyUnion in this case, that instantiates either a Contact or an Employee based on a type-discriminator. So the class constructor shown below uses an Enum value instead of a magic number as its type-discriminator, but it’s not doing any rocket science.

   internal ContactPropertyUnion(TypeEnum toBuild)   {      if (toBuild == TypeEnum.Contact)      {         this.wrapped = new Contact();         return;      }      if (toBuild == TypeEnum.Employee)      {         this.wrapped = new Employee();         return;      }      throw new ArgumentException("Unknown enum value.");   }

The ContactPropertyUnion class exposes all properties found on all the types that it wraps. Because not all wrapped objects actually implement every property exposed by the wrapper, each property does its own type-checking. If the wrapped object doesn’t implement the property two things happen:

  • The getter method returns a default value.
  • The setter method throws away the value.

For example, the following code shows how the wrapper class exposes the EmployeeNumber property. As you can see, it’s immaterial whether the underlying class actually implements the property?as a consumer, you can always pretend that it does without any penalty.

   internal int EmployeeNumber   {      get      {         int outVal = new int();         if ( wrapped is Employee )         {            outVal = (wrapped as                Employee).EmployeeNumber;         }         return outVal;      }      set      {         if (wrapped is Employee)         {            (wrapped as Employee).EmployeeNumber = value;         }         return;      }   }

You’d repeat code similar to the preceding EmployeeNumber property for every property exposed by all the wrapped classes, or at least every property that client code might be interested in.

Finally, just to make it easy on any UI code, the ContactPropertyUnion class exposes Boolean properties that return true when the wrapped class implements a given property.

   internal bool HasEmployeeNumberGetter   {      get      {          return wrapped is Employee;      }   }

Compared to the ubiquity of traditional polymorphism, the PropertyUnion pattern is something of a niche market. Here’s when to use it:

  • In the data-layer. Use a PropertyUnion as a lightweight substitute for an Inheritance Mapper when working with a “single table inheritance” data model.
  • On the UI side. Use a PropertyUnion when you don’t have a PropertyEditor control available?such as on Web Forms, for example?and your application’s users might need to edit a number of related types.
  • When working with framework types. Some developers might argue that my object model above is na?ve given the requirements. And they might be right, but you can’t always control the design of the objects you’re storing.

And here’s when not to use a PropertyUnion:

  • As an excuse for inappropriate inheritance. Remember that inheritance is very easy to overuse, so don’t use PropertyUnion as an excuse for inheritance when a more sophisticated pattern would be better.

The PropertyUnion pattern has some related patterns that you might want to explore if you’re not familiar with them already.

  • Factory. Because the PropertyUnion can create its wrapped object based on a type discriminator, the constructor works as a factory method.
  • Adapter. The PropertyUnion flattens out an inheritance hierarchy, making it easier to use with a flat, relational structure.

By now, the PropertyUnion technique should be looking pretty easy. It’s easy because it’s all boilerplate code?no algorithms to speak of, and no reflection or anything fancy at all. In fact, it’s so easy that you might be wondering whether there’s a way to automate the implementation of a PropertyUnion pattern. There is.

Part 2: Automating the PropertyUnion Implementation
Writing the simple ContactPropertyUnion class by hand worked out great, but there are a couple of lingering maintainability problems:

  • Any changes to the inheritance hierarchy require equivalent changes in the PropertyUnion class.
  • The type-checking could get pretty intricate with a more complex inheritance hierarchy, creating a potential source of bugs.
  • It’s kind of irksome that you have to write the PropertyUnion class in the first place. I mean, it’s a very simple implementation and everything that it needs?an inheritance hierarchy to wrap up, and some meta-data?is available to the compiler as type information at build-time.

So now I’ll show you how to implement a PropertyUnionBuider using .NET’s CodeDom namespace, the library designed for writing language-neutral code-writing code.

But first a word of caution. CodeDom lets you write code that writes code. Even better, your code-writing code can write code in any .NET language. So if you’re like me, your first reaction is to say, “Cool!” Unfortunately, it’s not always all that cool. In fact, the namespace can be a real hassle to work with for a number of reasons.

  • CodeDOM doesn’t support any language-specific idioms. The namespace represents the least common denominator of all .NET languages, so you can’t directly use handy C# keywords such as as and is. Instead, you’ll have to use framework methods, which can add verbosity and reduce readability.
  • CodeDOM is verbose. Vertically, it takes many more lines of code to write an algorithm with CodeDom than to write it directly in the .NET language of your choice. For me, the ratio is at least 3:1. But horizontally too, CodeDOM takes up a lot of room because many of the class names are really, really long? CodePropertySetValueReferenceExpression, for example. So even after you get comfortable working with the classes, it’s still hard to figure out what’s going on inside a block of code because the length of a single line can easily top 150 characters.
  • CodeDOM is hard to write. This is the kicker, and it will remain true even if Microsoft were to put a ton of work into making CodeDOM more usable? writing code-generating code is still a very different experience. You’re programmatically generating a parse-tree, and that requires a different mindset from standard development.

The point here is that CodeDOM is best used for generating simple code in multiple languages. Building a typed DataSet generator is a great application for CodeDOM, as is the PropertyUnion generator example in this article. But, if you need to generate fancy algorithms, or if you don’t need to generate code in multiple languages, then you’re probably better off using either XSLT, or a commercial code generator.

My approach here won’t be to hold your hand and take you line by line. I tried that, and the result was a very boring and tedious article. It was boring, I think, because CodeDOM code is a fairly tedious way to express concepts that are second nature to most programmers: types, variables, methods, algorithms. And for details, you’ll have to look at the sample application anyway. So instead, I’ll take you on a high-level tour of a working CodeDOM application.

Step 1: Some Preliminary Tools
You’ll find that you need to do a lot of type-manipulation to work with CodeDOM, so I’ve included a useful class called InheritanceTree (see Listing 1) that takes care of all the type-operations. Essentially, it’s a typesafe collection of Type objects, with some checks to ensure that users don’t try to place types that aren’t in the same hierarchy?for example, System.String and SqlDataReader?in the same PropertyUnion class.

You’ll also find a utility class, called Build, in the sample code. (I didn’t create the Build class right away, though I wish that I had.) The idea is to wrap up common CodeDom operations?even single line operations?to improve readability.

Take the CodePropertySetValueReferenceExpression class as an example. The CodeDOM approach represents language features as classes. This particular class represents a CodeDOM equivalent of the C# keyword value used in property setters. But, of course, you can’t use the value keyword because that’s C#-specific, so CodeDOM substitutes this class instead.

Using this kind of syntactic sugar is mostly personal preference. But, to me, this:

   new CodeAssignStatement(propRef, Build.Value);

Is easier to read than this:

   new CodeAssignStatement(propRef, new       CodePropertySetValueReferenceExpression() );

Just so you can see what all that boils down to, here’s the C# equivalent of the preceding CodeDOM statements:

   someExpression = value;

Step 2: Enumerating Types
You may remember from earlier in the article that you need a type-discriminator so that the data layer knows which types to instantiate. Fortunately, building an enumeration type is the easiest possible non-trivial CodeDOM operation?because really, an Enum is just a class with only data-members. You’ll start to notice a theme when working with the CodeDOM namespace: structure is easier than content. And enums are all structure.

The snippet below includes all the code you need to build an enum: just loop through all of the types, and add a member for each one.

   TypeCollection allTypes =       inheritanceTree.GetAllTypes();   _typeEnum = new CodeTypeDeclaration( "TypeEnum" );   _typeEnum.IsEnum=true;      for(int i = 0; i

Step 3: Construction
Next, you have to write the code that outputs constructors. The default constructor is easy enough to build:

   private CodeConstructor BuildDefaultCtor()   {      CodeConstructor defaultConstructor = new          CodeConstructor();      defaultConstructor.Attributes =         MemberAttributes.Private;         return defaultConstructor;   }

And here's the output:

   private ContactPropertyUnion()   {   }

A non-trivial constructor takes a little more effort. The example below takes a reference to a wrappable object, and assigns it to a data member. Remember our theme: In CodeDOM, structure is easier than content. Now, we're getting into some content. This method you're trying to output has just one line of code, but writing it with CodeDom takes a dozen:

   private CodeConstructor BuildWrapperCtor()   {      CodeConstructor wrapCtor = new CodeConstructor();      wrapCtor.Attributes=defaultVisibility;            //Setup the single parameter.      CodeParameterDeclarationExpression toWrapParam =          new CodeParameterDeclarationExpression();      toWrapParam.Name="toWrap";      toWrapParam.Type = new CodeTypeReference(          inheritanceTree.Root );      wrapCtor.Parameters.Add(toWrapParam);         //Assign the parameter to the data member.      CodeAssignStatement assign = new          CodeAssignStatement();      assign.Left = WrappedFieldReference;      assign.Right = new CodeVariableReferenceExpression(         toWrapParam.Name);      wrapCtor.Statements.Add(assign);         return wrapCtor;   }

Here's the output:

   internal ContactPropertyUnion(Contact toWrap)   {      wrapped=toWrap;   }

Step 4: Properties
Finally, you're getting to the heart building PropertyUnion classes automatically. Essentially, every property getter has four steps:

  1. Initialize a default variable. (Just in case the wrapped object doesn't implement the property.)
  2. Check the type of the wrapped object.
  3. Depending on the type, cast the wrapped object to its concrete type and invoke the appropriate property on the cast object.
  4. Return the resulting value.

That's not too bad. These five steps contain mostly content?such as conditionals and property or method invocations. Listing 2 builds an accessor based on a PropertyInfo object from the System.Reflection namespace. It's long, but at least you only have to write it once.

So that's the high-level CodeDOM picture. Of course there are some details, things like comments, and building defaults, so take a look at the sample application for details. But most of the work takes place in the snippets above. Even more than elsewhere, keeping things simple really pays off when working with code generation.

I'm not going to tell you that the PropertyUnion is the greatest design pattern since the Singleton. It's not. But it can be a good way to centralize type-checking-ugliness in a single class, especially when you're doing ugly type-checking in different layers of your application.

The real point is this: If you can reduce a pattern down to its bare, mindless essentials, if you can simplify it until it becomes simplistic, then you can probably build a reusable implementation with code generation.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist