By Mike Gunderloy
Reduced to its essentials, the job of a contract developer is pretty simple: Produce high-quality code that meets your customers' needs in the shortest possible time. There are many tools that can help you attain this goal. Requirements-management, bug tracking, source code control, and automated testing tools, for example, have their place in many offices.
One helpful tool that you may not have considered is a code generation tool. In this article, I'll show you some of what's available in that category for developers who use the Microsoft .NET platform. Depending on your needs and your applications, you may find that you can automate large sections of code that you're used to writing by hand.
Everyone's doing it
Let me quickly dispel the notion that code generation is strictly for inexperienced developers or those who don't really know what they're doing. In fact, if you're using any modern IDE, you're already using code generation tools, though often they're not explicitly labeled that way.
For example, when you add a new Windows form to a C# application, Visual Studio .NET generates 69 lines of code for you to handle the basics of referencing the appropriate libraries, declaring a namespace, instantiating and disposing of the form, and so on. Sure, you could write the equivalent code by hand, but why would you?
Visual Studio .NET features other code generators that are even more hidden. For example, what happens when you add a typed DataSet to your C# application and then drag a table to the XSD designer? The IDE automatically builds a class for you, based on the information in the designer.
Click the Show All Files button on the Solution Explorer toolbar to see the class. Even for a relatively simple DataSet (for example, one based on the sample Northwind Customers table that comes with Microsoft Access), you'll find that VS.NET generates 650 lines of code that you don't need to write yourself.
One of the great things about the .NET environment is that it's very easy to automate. There are well-documented ways to plug additional code generation tools into Visual Studio .NET, and many vendors have taken advantage of these hooks.
I can't possibly describe every .NET code generation tool in one short article, but I can give you a look at a few interesting ones. One way to think about code generation tools is by the amount of code they generate. In this article, I'll look at three broad categories:
- Simple code generators that build one or a few objects
- Data access layer generators that tie your application to a database
- Application generators that build a full solution
Depending on your needs, each of these types may have a place in your toolbox.
Simple code generators
As an example of the sort of problem that code generators can help you solve, consider the common requirement to maintain a collection of instances of a class. The .NET Framework provides classes such as System.Collections.HashTable and System.Collections.SortedList to make creating a collection very easy. But these classes only contain raw objects. That means that if you use, say, a SortedList to contain Customer objects, you'll be constantly casting back and forth from Customer to Object. Worse, there's no protection against accidentally storing an Order object in the list of customers.
Of course, you can derive your own class, say CustomerSortedList, from the base SortedList class and override all of its methods so that they explicitly take a Customer object. But why should you do all that work?
Enter .NET CollectionGen from Chris Sells. This code generator is implemented as a custom tool that integrates with Visual Studio .NET, just like the built-in DataSet designer. After you install CollectionGen, all you need to do is define your needs in a simple XML file:
<?xml version="1.0" encoding="utf-8" ?>
Set the Custom Tool property of the file to SBCollectionGen, save your project, and you'll get the desired class. For instance, the generated class will implement a Contains method that takes a string rather than an Object as a key:
Public Overridable Function Contains(ByVal key As string) As Boolean
' Determines whether the CustomerSortedList contains a specific key.
' key = The key to locate in the CustomerSortedList.
Return (IndexOfKey(key) >= 0)
This is a typical pattern when using a code generator. Rather than write the code, you supply metadata defining the code that you'd like to have written (here, the template to use, the data types for the key and item, and the name and namespace for the collection). Based on this metadata, the code generator builds the code for you. In this case, you trade writing 10 lines of metadata for over a thousand lines of code.
CollectionGen is very tightly targeted to a single task: building type-safe collections. For a more flexible tool, take a look at Eric J. Smith's CodeSmith. CodeSmith uses its own syntax (very similar to that of ASP.NET pages) for templates. Here's a tiny piece of a template to generate SQL stored procedures:
<%@ Property Name="SourceTable" Type="RedRiver.SchemaExplorer.TableSchema" Category="Context" Description="Table that the stored procedures should be based on." %>
<%@ Property Name="IncludeDrop" Type="System.Boolean" Default="True" Category="Options" Description="If true drop statements will be generated to drop existing stored procedures." %>
<%@ Property Name="IncludeInsert" Type="System.Boolean" Default="True" Category="Options" Description="If true insert statements will be generated." %>
At runtime, CodeSmith translates this syntax into the user interface shown in Figure A. You then fill in each property specified in the template file and click the Generate button. CodeSmith uses the information you enter together with the rest of the template (which can contain quite complex logic) to generate the code. Although it can take a while to understand how it really works, CodeSmith ends up being a general-purpose tool to generate code in any language you can think of.
Building the data access layer
A large number of applications depend on moving data from a database to a user interface and vice versa. Indeed, it seems reasonable that the majority of business applications are concerned with manipulating databases. It's not surprising, then, that there are many products out there to make it easier to connect a database to a user interface.
One product in this category is ORM.NET from Olero Software. With data access layer generation, typically you select metadata by interacting with a database rather than entering it by hand. Figure B shows ORM.NET in action. After connecting to a database, you can use the ORM.NET interface to investigate the objects in the database and set properties that customize the generated code.
After setting options, you just click the Generate Data Layer button and specify a folder for the generated files. ORM.NET builds an entire Visual Studio .NET solution with a class library for data access and a test application. You can use the class library in any other application to abstract the entire database to objects, as shown here:
DataManager dm = new DataManager(Config.Dsn);
Customers c = dm.NewCustomers("DC Company", "DCCOM");
c.City = "Endicott";
c.Region = "Washington";
The major benefit of a product like ORM.NET is to take tricky but routine code for data access and hide it. You don't need to be a SQL or ADO.NET expert to use the generated classes to retrieve data from the database or add new data to it. All you need to do is understand the interface that ORM.NET builds for you.
Other products that specialize in generating data access layers include OlyMars, TierDeveloper, and LLBLGen.
Applications at the click of a button
If generating a data access layer is good, how much better would it be to create an entire application without writing any code? You can find out the answer by using application generators, which are typically the most complex (and expensive) of code generation products.
For example, Figure C shows part of the Application Builder in CodeCharge Studio 2.0. CodeCharge is typical of application builders in that you select a database connection, pick the tables you care about, decide which forms to build for each table, and choose a visual theme. The tool then generates everything you need to form a complete ASP.NET application (CodeCharge can also target other platforms such as Cold Fusion).
CodeCharge Studio goes beyond some other application generators in that it provides its own IDE to let you further edit the application's code after creation. When you're satisfied with the results, you publish the project to your Web server, and CodeCharge creates all of the necessary pages and class files.
Other tools in this category include DeKlarit and Dataphor.
So, is it for you?
If you think about the entire spectrum of code-generation tools, there are clear trade-offs between how much code the tool writes for you and how general-purpose that code is. At one extreme, you have the simple code generators that can automate the grunt work of things like collection classes. You can use a tool like CodeSmith to automate just about any repetitive programming task, but you'll need to put the classes together into an application yourself.
At the other end of the spectrum are tools such as CodeCharge Studio, which is great for building typical line of business applications that take a database to a Web user interface, but doesn't offer the flexibility of a general-purpose programming language. The more code the tool generates, the more you'll need to be sure that its end result is what you want.
Some developers seem to be prejudiced against all code generation tools, insisting that they can write better code by hand. While that may be true, you need to be very careful how you define "better." Remember, the goal is to deliver working applications to your clients as quickly as possible. There are no brownie points in general for delivering elegant source code, or source code that runs in the least possible space, or perfectly optimized source code.
With the exception of some limited realms of programming (such as development for embedded devices), it's often preferable to ship slightly suboptimal code today, as opposed to waiting for perfect code tomorrow. Choosing the correct code generator can help you meet that goal.