Rants, rambles, news and notes from another geek

CodeSmith + CodeDom

Yesterday Steve Eichert wrote:

This past week while working on a re-write of the framework that prompted the initial evaluation of Emit vs. CodeDOM I decided to revisit the debate.&nbsp_place_holder; After “thumbing” my way through the documentation of the CodeDOM classes I came across the ICodeCompiler.CompileAssemblyFromSource() method.&nbsp_place_holder; Rather then emitting OpCodes I decided to create a couple of templates for my code and use the ICodeCompiler to genearte an in memory assembly.&nbsp_place_holder; While I did miss the process of pushing things onto the call stack using OpCodes, I realized how much more productive I was creating “C# templates” for my code and compiling them with the CodeDOM classes.

As I started to think about this, I decided to try this out. Since CodeSmith will produce a template assembly that I can call from code, this should be easy as pie!

I started by creating this simple CodeSmith template:

**<%@ CodeTemplate Language="C#" TargetLanguage="C#" Description="" %>**
**<%@ Property Name="ClassName" Type="System.String" Default="MyClass"  
    Category="Options" Description="" %>**
**<%@ Property Name="SampleStringProperty" Type="System.String"  
    Default="SampleString" Category="Options" Description="" %>**
public class <%=ClassName%>
   public string <%= SampleStringProperty %>   
      get { return _<%=SampleStringProperty%>; }
      set { _<%=SampleStringProperty%> = value; }

   private string _<%=SampleStringProperty%>;

Then using CodeSmith, I created an assembly from my template. Basically this creates a DLL that you can reference from your code that contains the code generation class defined by the CST file.

Once that was done, I created a simple forms app, added a button, and put the following code in the click handler:

private void button1_Click(object sender, System.EventArgs e)
   CodeSmithTemplates.SimpleTemplate template = 
      new CodeSmithTemplates.SimpleTemplate();
   template.ClassName = "MyClass";
   template.SampleStringProperty = "MyProperty";
   string source = template.RenderToString();

   CSharpCodeProvider cSharpProvider = new CSharpCodeProvider();
   ICodeCompiler compiler = cSharpProvider.CreateCompiler();

   CompilerParameters options = new CompilerParameters();
   options.GenerateInMemory = true;

   CompilerResults results = 
      compiler.CompileAssemblyFromSource( options, source );

   if( results.Errors.HasErrors )
      StringBuilder sb = new StringBuilder();
      foreach( CompilerError error in results.Errors )
         sb.Append( String.Format("{0} - {1}\n", error.Line, 
            error.ErrorText ) );

      MessageBox.Show( sb.ToString(), "ERROR" );

   Assembly assembly = results.CompiledAssembly;
   object o = assembly.CreateInstance( "MyClass" );

   Type t = o.GetType();
   t.InvokeMember( "MyProperty", BindingFlags.SetProperty, null,
      o, new object[] { "Test!" } );
   object result = t.InvokeMember( "MyProperty", 
      BindingFlags.GetProperty, null, o, null );

   MessageBox.Show( result.ToString() );


That’s it! Granted I didn’t do anything particularly complex with it, but it is a very effective way to combine template generated code (a la CodeSmith) with CodeDom. Enjoy!