What is the Dominator?
11:48 AM Friday Feb 1, 2008 Comments: 0I went through some different iterations trying to figure out a good way of generating code. I've seen some generators that used text files and did keyword replacements. I didn't want that baggage. And I wanted to be able to generate code in any .NET language. Having separate versions of text templates, a set for every .NET language is lame.
CodeSmith rocks as a templating engine. The main setback is that it's not free. Another setback to using CodeSmith was that you end up generating templated code and limiting any extension points to the generated code(without actually modifying the templates). Essentially I wanted to stay away from string replacement text file templates.
Since I'm pretty familiar with the CodeDom I set out to roll my own. Here is the class diagram for what I came up with, the Dominator:
An Example: My.Data.Customer
Here's a good example of what the Dominator can do. Let's suppose want to generate some code in the namespace My.Data. The namespace will contain a single class, Customer, with string property FirstName that is read/write. The class should look like this:
namespace My.Data {
public class Customer {
private string _firstName;
public virtual string FirstName {
get {
return this._firstName;
}
set {
this._firstName = value;
}
}
}
}
| |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
Generating Code with System.CodeDom
If I was just to stay within the confines of System.CodeDom, the code needed to write the Customer class would look like this:
CodeNamespace nsdecl = new CodeNamespace("My.Data");
CodeTypeDeclaration cdecl = new CodeTypeDeclaration("Customer");
CodeMemberField field = new CodeMemberField(typeof(string), "_firstName");
cdecl.Members.Add(field);
CodeMemberProperty prop = new CodeMemberProperty();
prop.Name = "FirstName";
prop.Attributes = MemberAttributes.Public;
prop.Type = new CodeTypeReference(typeof(string));
prop.GetStatements.Add(
new CodeMethodReturnStatement(
new CodeFieldReferenceExpression(
new CodeThisReferenceExpression(), "_firstName")));
prop.SetStatements.Add(
new CodeAssignStatement(
new CodeFieldReferenceExpression(
new CodeThisReferenceExpression(), "_firstName"),
new CodeSnippetExpression("value")));
cdecl.Members.Add(prop);
nsdecl.Types.Add(cdecl);
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
codeProvider.GenerateCodeFromNamespace(nsdecl, Console.Out, new CodeGeneratorOptions());
| |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
Bring on the Dominator
Generating the same class using the Dominator would look like this:
ClassDeclaration cdecl = new ClassDeclaration("Customer");
cdecl.AddProperty("FirstName", "_firstName", typeof(string));
new CodeBuilder().GenerateCode(Console.Out, "My.Data", cdecl);
| |
1
2
3
|
Generating a Castle ActiveRecord Class
The expected generated code is this:
public class Customer : Castle.ActiveRecord.ActiveRecordBase<Customer> {
}
| |
1
2
|
CodeTypeDeclaration cdecl = new CodeTypeDeclaration("Customer");
cdecl.BaseTypes.Add(
new CodeTypeReference("Castle.ActiveRecord.ActiveRecordBase",
new CodeTypeReference("Customer")));
new CSharpCodeProvider().GenerateCodeFromType(cdecl, Console.Out,
new CodeGeneratorOptions());
| |
1
2
3
4
5
6
|
ClassDeclaration cdecl = new ClassDeclaration("Customer")
.InheritsFrom("Castle.ActiveRecord.ActiveRecordBase", "Customer");
new CodeBuilder().GenerateCode(Console.Out, cdecl.ToCodeDom());
| |
1
2
3
|
Code
Here's some sample code that demonstrates the code in this post, very simple stuff, download it here.
Before creating the Dominator I checked out Refly and for some reason that I can't remember today, it didn't work for me. It sure looks like it does what I need but I need to look at it some more.
