Click here to Skip to main content
Email Password   helpLost your password?

Introduction

The hereby provided contribution is a command-line oriented template based code generator. Both source code and executable are made available for you to use, enhance and extend.

The code generator provides the following features:

Before we take a look at a sample, some thoughts about code generation.

Why Code Generation?

I have been using code generation for several years, and have seen it be an important key to the success of the projects. By generating code, sometimes over 20% of your projects, you make sure that whole layers of your implementation are consistently written and with no exception conform to the architectural requirements.

Code generation assures a high quality of code as the generated code contains no copy-paste errors or other errors often encountered in manually written code.

Code generation also provides you with a very high maintainability of the code. If you generated code based on a database structure, you only need to "press a button" to have the code again in sync whenever the database structure changes.

On the other hand, if you want to perform an architectural change, for instance have all your data objects implement INotifyPropertyChanged (in the System.ComponentModel namespace), you only need to change the generating template, then "press the button" again...

But for these benefits to come true, you must follow two important rules when using code generation on your project:

Rule 1: Make Sure You Can At All Times Regenerate All Generated Code

This implies that you should avoid 'interactive code generation' (where you choose a menu option in your IDE, then enter some data in a form and have a code file generated, or even worse, a piece of code). Interactive code generation can be a handy addition to your coding toolkit, such as snippets and other IDE features. It increases your typing rate, but not the maintainability of your code.

Because of rule 1, you should also never manually modify generated code. Therefore, the generated code must closely match your needs (see also rule 2) so that you don't need to change it.

You can however generate code that is adaptable and extendable. For instance, declare the generated methods virtual so you could still create a subclass and override methods, or declare your classes partial - this will allow you to extend them by simply adding an additional, non-generated file.

Rule 2: Make Sure You Generate Your Code Based on an Extendable Source

Because of rule 1, the generated code must closely match your needs. You must therefore make sure that all current and future requirements will be supported.

A common transgression of this rule is to generate data objects based on database table definitions. Although database table definitions are a good starting point to generate data objects from, it is not a place where you can indicate which generated property should be made "public" and which should be made "internal". Whenever I would have the requirement to specify the visibility modifiers of the generated properties, I would come into trouble.
Good sources for code generation include:

As an alternative to generation based on database definitions, you could mix database definition data with information stored in 'meta'-tables stored in the database itself.

This brings us to the code generator available here. It is made with those two rules in mind.

To match the first rule, the generator does not interactively integrate within your IDE. Instead, it's a command-line tool and all arguments can be stored in a file. This way, you can rerun the code generation on your project with a single command on the command prompt!

As of the second rule, simple, the code generator makes no assumption whatsoever about the generation source. So it provides no default source to generate from, and you'll have to create and provide your own source.

Keep in mind that whatever source you choose, it should be a source you have full control over. You must be able to extend the information stored in the source to extend the capabilities of the code generation.

Making of the Generator

This article is not about the making of the code generator. However, the source code can be downloaded on this page, and to enlighten you on the code structure, here is some information about the code generator implementation.

At the core of the parser, you'll find an enhanced version of the Mixed Content Parser I presented in another article.

The Resources folder of the Arebis.CodeGenerator project contains templates for the generated code (C# and VB.NET).

The whole generation is run in a separate AppDomain with ShadowCopyFiles enabled. This was needed to allow deletion of the generated assembly files. This could have been avoided by setting GenerateInMemory to true on the CompilerParameters passed to the CodeDomProvider used to compile the template, but then it seems the templates would not have the same debugging capabilities.

By adding line pragmas (#line in C#, or #ExternalSource in VB.NET) to the generated code, both compile time and runtime errors are reported by means of the original template source line numbers.

A Sample Project

As a sample project, we will generate code based on information stored in XML files.

The Input Files

Take for instance the following XML file, as a definition for a Person class:

Person.xml file

The referenced Address class could also be defined, for instance with the following XML:

Address.xml file

First Template (BuildClasses.cst)

With "one press of a button", we want to be able to generate class definitions for those XML files. To achieve this, we need to write some templates. The first template that we write will read the XML files and ask another template to generate the classes:

BuildClasses.cst file

We name this template BuildClasses.cst. (I like the extension CST for "C Sharp Template".)

Lines 1 to 5 contain directives. The first directive, CodeTemplate, is required and tells the generator the code in the template is written in C#.

ReferenceAssembly provides references to assemblies to be referenced. Provide either the full path of the assembly, a relative path if the assembly is located near the template, or just the filename if the assembly is in the GAC or in the same directory as the code generator itself. Note that the mscorlib.dll, System.dll, and Arebis.CodeGeneration.dll are referenced by default, however, duplicating the reference to System.dll does not harm.

The Import directive imports namespaces to the code, so that you don't need to specify full classnames.

The remainder of the template, lines 6 to 20, is a scriptlet � a piece of code executed inline. The code is written in C# as specified by the CodeTemplate directive.

At line 8, this.Settings is used. All settings provided to the code generator (both in a settings file or on the commandline) are available to the Settings property of the template. The property is of type System.Collections.Specialized.NameValueCollection. The interesting thing about the NameValueCollection is that it relates keys (strings) to either a single string, or an array of strings.

this.Host on line 13 gives access to an Arebis.CodeGeneration.IGenerationHost. The CallTemplateToFile() method allows to call another template, and have the output written to a separate file. Additional parameters can be passed to match parameters of the called template. In our case, we pass an XmlElement matching the document element of the XML file.

The IGenerationHost defines the following methods to call templates:

void CallTemplate(string templatefile, params object[] pars);
void CallTemplateToFile(string templatefile, string outputfile, 
    params object[] pars);

The template itself will be compiled to a class inheriting from CodeTemplate. An overview of the main classes and interfaces is defined in Arebis.CodeGeneration:

Screenshot - codegen_fig1.gif

Second Template (Class.cst)

The second template, Class.cst, will generate individual class files. The template is the following:

Class.cst

Again we start with directives. The CodeTemplate directive contains some additions (ClassName and CodeFile), more on this later on.

We have also a Parameter directive telling us this template requires one parameter, of type XmlElement, which will be accessed from the template code by the name classElement (as in line 12).

The remainder of the template is literal content, mixed with inline expressions (between <%= and %>), as well as 2 scriptlet parts, from line 14 to 17, and on line 26.

At line 19 we access a local method ToCamel(). This method does not exist by default. However, in the CodeTemplate directive, we have provided a CodeFile. The CodeFile is a partial class that will be used as part of the compiled template. Whenever the CodeFile directive attribute is used, you must also specify the ClassName attribute on the CodeTemplate directive (line 1), as the generation engine needs to give the generated class the exact same name as your class in the codefile.

The codefile Class.cst.cs provides a ToCamel() method on partial class names Template.Class. Therefore we define the Class.cst.cs file as follows:

Class.cst.cs file

Code (behind) files allow in making the code of the template easier to read as you can put complex logic away in the code behind file.

Running the Sample

The sample is almost ready to be run. The only thing we need to provide are the settings. The Code Generator command-line tool expects an argument being a settings file, and/or settings given on the command-line.

The easiest way to run the sample would be:

CGEN /template "BuildClasses.cst"

We provide the CGEN command-line tool with a value for the template setting. This is the only really required setting.

But as we use settings from within the templates, we need to provide values for those settings also. We need to provide a value for the source setting (used in the first template, line 8), and for the namespace setting (used in the second template, line 10).

In addition, we could provide values for the targetdir and the logfile settings. For information on those, and other settings, type CGEN /?. The settings file is now:

BuildClasses.settings file

We can now run the code generator, passing it only the settings file:

CGEN BuildClasses.settings

The result, two files generated in the Result\Domain directory, of which one looks like:

Result\Domain\Person.generated.cs file

As you can see, the generated class is declared partial. This allows us to create a separate, non-generated file, to customize the behavior of the generated class.

Try the sample out yourself, it is available for download on this article. Download CGenSample.zip, extract it somewhere, and run the RunSample.cmd batch file to execute the sample. Then feel free to modify the templates or other files to see the effect, add syntax or runtime errors to see how errors are reported, or debug the execution of a template as described in the next paragraph.

Debugging Templates

By adding a call to System.Diagnostics.Debugger.Launch(), as done on the next screen (line 7), you can launch a debug session on the execution of your template.

Screenshot - debug_fig1.gif

Template Syntax

The following provides you information about the template syntax, including the list of directives and their attributes.

Directives

Directives contain information about the template, and about what is needed for the template to compile successfully. Directives provide information about the language of the template, required assembly references, namespaces to import, etc.

Templates must start with one single CodeTemplate directive, and can have additional directives. Although not mandatory, it is strongly advised to put all directive declarations in front of the template file.

Directives look like HTML tags (they have a name and usually have attributes), but are written between <%@ and %> markers.

CodeTemplate Directive

Description

Provides template declaration and meta data. Each template should have one and only one CodeTemplate directive at the start of the file.

Attributes
Language The template code language. "C#" or "VB". If not specified, "C#" is assumed by default.
TargetLanguage Optional. The target language, the language of the output. Can be any value.
AssemblyFile Optional. The name of the assembly file to be generated. Could be used to reference this assembly from within other templates.
ClassName Optional. Full name of the class to be created for this template. Setting is mandatory if using a CodeFile file.
CodeFile Optional. Code behind filename containing a partial class to be completed by the template.
Inherits Optional. Base class of the template class. Must be of type Arebis.CodeGeneration.CodeTemplate.
LinePragmas Optional. Whether to output line pragmas and so provide debugging information in terms of the template. True by default.
Explicit Optional. VB only. "On" or "Off", whether the Explicit option should be set on or off. Off by default.
Strict Optional. VB only. "On" or "Off", whether the Strict option should be set on or off. Off by default.
Description Optional. Free description of the template.

ReferenceAssembly Directive

Description

References an assembly file and provides an absolute or relative filename path. The assembly should be present in the same directory as the template, a bin subdirectory, any directory passed to the referencepath setting or in the GAC.

Attributes
Path Mandatory. The path (absolute or relative) of the assembly file.
Notes

Note that the assemblies mscorlib.dll, System.dll and Arebis.CodeGeneration.dll are automatically referenced.

Import Directive

Description

Imports a namespace in the template code source.

Attributes
Namespace Mandatory. Namespace to be imported.
Alias Optional. An alias for the namespace imported.

Parameter Directive

Description

Declares parameters of the template.

Attributes
Name Mandatory. Name of the parameter (must be a valid .NET identifier)
Type Type of the parameter (System.Object is assumed by default)

CompileFile Directive

Description

Provide additional files to be included in the template compilation. By default, the translated template and its eventual codebehind files are compiled. This directive allows for specifying additional files.

Attributes
Path Mandatory. Absolute or relative path to the file to include in the compilation of the template assembly.

Expressions

Expressions are written in the language of the template (C# or VB), and are evaluated in place. Their result is converted to string and written to the output of the template.

Expressions are written between <%= and %> markers.

Code Blocks

Code blocks contain code that is executed in place. They can call methods, contain conditional (if) statements, loop definitions, etc. In fact they can contain any code that would be legal in a method of the compiled template.

Code blocks are written between <% and %> markers.

Function Blocks

Within templates, you can define one or more function blocks in which methods and other class-level elements can be placed. These are not executed in place, but are defined in the compiled template class and can contain methods called from within code blocks.

Function blocks are written between <%% and %%> markers.

Commenting Templates

Comments within templates can be written between <%-- and --%> markers.

Including Files

Files can be included in templates as if they were part of it. Include files can contain any content part valid in templates.

Include files are specified with the following notation:

<!--#include file="filename"-->

The path is either absolute or relative to the template file.

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralCongrats and a wish list item
Tiago Freitas Leal
7hrs 12mins ago 
Hi Rudi,

Congratulations! Very nice work indeed.

I only miss a "Property" directive that would allow me to return a value (like an error description) to the calling code. I guess "Parameter" won't do the trick...
GeneralMy.MySettings.Default
Thomas2905
5:38 19 Nov '09  
how can i use my.settings within this template?
GeneralChange to generate non .Net Code
shortdog
9:53 5 Jun '08  
I am need of a code generator like this but I need to output ANSI C code. I've started looking at your source code. Any tips on changing it to output ANSI C. For instance :

<![CDATA[<%@ CodeTemplate Language="C#" ClassName="Template.Class" CodeFile="Class.cst.cs" %>]]>
line in the Class.cst indicates that it is a C# File. Does the result file try to get compiled? Can I for it to us a Non .Net complier?

Sorry, I am new to the code generation area and might be missing some key concepts.

Thanks
GeneralRe: Change to generate non .Net Code
Rudi Breedenraedt
0:19 6 Jun '08  
Dear shortdog,

You should make a distinction between the code that generates, and the code that is generated.

The code that generates, that is, the code you write between <% and %> markers and alike in the templates, and the code you write in codebehind files of the templates (like the one refered to by CodeFile="Class.cst.cs") has to be written in C# or VB.NET (hence Language="C#" fir instance).

However, the code that is generated, in other words, the result of the generation process, can be virtually any textual like output, such as programming code (any language including .NET languages, Ansi C, assembler, lisp, PHP, whatever you like), but also XML, TXT, RTF, etc.

So in fact, it should not be called a 'code generator' but a 'text generator'...

One issue you might come into however, is that the generated files are written in UTF-8. I don't know if this could be an issue for your Ansi C compiler. I might include a feature to allow choosing the encoding of the outputted files, but for now there's no such function available.

Kind regards

Rudi Breedenraedt
GeneralCan I Generate Methods also with this Code generator
Shailspa
1:31 10 Dec '07  
Hi All,

Can I generate Class methods also with this Cpde Generator,If yes then How.if Any body can give me a idea.

Thanks & Regards
Shail Smile
GeneralRe: Can I Generate Methods also with this Code generator
Rudi Breedenraedt
12:21 18 Jan '08  
Of course, you can generate whole classes, including methods and properties.
You can even generate SQL, XML, text, RTF,... any textual output.

Or did you mean something else ?
GeneralThe type or namespace name 'TeamFoundationServer' could not be found (are you missing a using directive or an assembly reference?)
Shailspa
20:36 9 Dec '07  
Hi All,

I am getting following error to complile this code "The type or namespace name 'TeamFoundationServer' could not be found (are you missing a using directive or an assembly reference?)".

Here when I tried to add this reference I didn't find thid assembly in Private assemebly folder & not in Public assembly folder on following path C:\Program Files\Microsoft Visual Studio 8\Common7\IDE/Private assemebly & C:\Program Files\Microsoft Visual Studio 8\Common7\IDE/Public assemebly D'Oh! . What should I do to get this assembly in my Program.I am missing all type of TeamFoundation assemblies in my VisualStudio Enviornment.

I am new to .Net. Please help me.

Shail
GeneralRe: The type or namespace name 'TeamFoundationServer' could not be found (are you missing a using directive or an assembly reference?)
Rudi Breedenraedt
12:26 18 Jan '08  
Hello Shailspa,

Sorry for the late reply. I haven't been online for some time due to personal issues.

The issue you have with the namespace TeamFoundationServer is related to the Visual Studio edition you are using. If you do not have the Team Edition, you will be missing some assemblies (indeed, those in the ...IDE\Public folder.

The simplest way to get rid of this error, is to remove the "Arebis.CodeGeneration.VisualStudio" project from the solution. Everything should then compile fine.

Regards

Rudi
GeneralVery nice and thanks
Bert delaVega
9:32 21 Nov '07  
I was about to start on the monotonous task of creating 38 property classes for 38 tables. Then I saw your article, generated the xml, modified the template and I now have 38 classes in less than an hour!

Nice piece of code that I'll find quite a few uses for.
Thanks again,

Bert


GeneralFeedback
Dmitri Nesteruk
6:34 21 Nov '07  
Your code generator is interesting. I use XSLT, myself, but recognize that sometimes the level of control it provides is a little less than what I would like. One issue I have with it is that I sometimes need to interact with code as Model instead of Text, i.e. work with it the way refactoring tools (e.g., ReSharper) do. And I think the only solution to this is to create something like what you're describing here. Of course, any CodeDOM-style operation on code is very difficult, so at the moment I'm only playing with the idea.

Dmitri
GeneralWow
Ben Daniel
12:39 20 Nov '07  
This is probably the best template driver code generator I've seen on CodeProject - well done! It really makes me wish I hadn't splurged big bucks on CodeSmith just a few weeks ago! Cry

Thanks,
Ben Smile

GeneralFeedback and a minor suggestion
Mike Mestemaker
14:06 11 Nov '07  
Up until now, Code Generation has seemed a bit more than my development team needed. Which is to say, more complex than we could easily evolve into our product development. This, on the other hand, seems simple enough to get us started and yet has enough power that we can use it going forward as well.

Oh, and thanks for making sure it supports VB as well as C#. That'll make it easier for others in my team to use. We have some C# experience, but it's mostly VB.

I'm planning to integrate this into our build process (NANT + custom control service) so that it automatically generates code when template/data files are checked into source code control. My suggestion is that you change the code so that all errors generate an error level to the calling program. As written, only those errors in the main command line app will trigger a failure in the calling program. Failures in the dll's are caught in the dll and the calling programs thinks everything's ok. Basically, it looks like you just need to change a couple of your main routines to return an integer and then return then via Environment.Exit when they return.

You don't state it explicitly; are there any licensing issues I should be aware of before integrating this into our build process? I hate to assume it's ok to use if it isn't.


- Mike
GeneralRe: Feedback and a minor suggestion
Rudi Breedenraedt
12:40 16 Nov '07  
Hello Mike. Thanks a lot for your feedback.

An update of the article, as well as of the downloads is on its way. Aside some fixes I've done in the parser, I have also made the 1.2 version return errorlevels as you requested. Errorlevel 1 is returned on runtime errors and errorlevel 2 is returned on compile errors.

Furthermore, I will soon add a message explaining how you can have files that are generated, automatically added to source control (Team System), or checked out from source control. One could also write extensions to support other source control systems.

About licensing: it's absolutely free to use, so don't hesitate. You can modify it or extend it as you which and even redistribute it, as long as my original copyright is left visible on the help screen (/?).

Hope you and your team will enjoy it !
GeneralRe: Feedback and a minor suggestion
Mike Mestemaker
12:56 16 Nov '07  
I did see the hooks for Source code integration; we're using Sourcegear's Vault product, so I'd have to write that myself. I actually removed that dll from the build because I don't have Team System and it wouldn't compile because it couldn't find the Team System references.

I'm not sure that part of it is critical to me. Basically, we're going to check settings files, templates and xml source files into Source control but not the generated output. Then, I'll setup our Build Server to autorun cGen first when it detects one of the xml or templates has changed. That'll regenerate the .vb files on the build server which will cause it to detect the need to build the related vb project files.


My first use of it was to generate a series of classes that implement data caching for specific tables/views. Normally the caching I do is an abstract class that inherit from so my actual classes are directly related to the data tables or views I'm querying from. i.e. specific properties for each field in the dataset. The basic coding pattern is repetitive and totally suited for code generation. Now that it's done, creating a new cache implementation is a matter of creating a new xml file and adding it to the settings file, so that's easy. And, adding new functionality to all of my caches can be done easily too. It's worked out really well for that purpose.

My 2nd use is a small template that lets me track status meeting agenda items in an xml file and generate my status meeting agenda based on which items in the xml file meet the date range covered by that meeting (not exactly "code" generation, but you did say it could generate any type of text). I guess that use is closer to using it like a scripting language than true code generation, but it works.

Probably more than you wanted to know. Thanks again for the great tool and article.


Last Updated 20 Nov 2007 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010