Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Using CodeDOM Code Generation to Implement Repetitive Patterns

Implementing simple patterns is often boring, time-consuming, and error-prone—just because they're so simple. But by using code generation you can prevent a lot of the drudgery.


advertisement
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.
ContactType FamilyName GivenName BirthDate Title Salary MailStop
Contact Bloggs Joe 1977.07.03      
Contact Pilgrim Billy 1924.02.12      
Employee Holden Judge 1822.01.01 President 10 12d
Employee Slothrup Tyrone 1922.03.12 Peon 3 44f

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.



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap