Discover Dynamic Code Compilation

Not sure where dynamic code compilation makes sense? A common scenario should help illustrate the need for it. Suppose you have to pull data out of one database and put it into another. Piece of cake: You’d just use a SQL statement to extract from the source and insert into the destination, right? What if you were copying production data to create test data and needed to alter the data to ensure that the destination data was secure to use in development? You may build a DTS or some other transfer mechanism, but if you do this across enough data it becomes time consuming to build data-scrubbing mechanisms each time you copy data. You could write an application to process and create the test data, but each time you use it on a different application you’ll have to alter and create new algorithms.

Enter dynamic code compilation. Rather than continually writing a bunch of throwaway code, you could create an application that has some inner workings to transfer data and apply code snippets to alter the data during the transfer. The code snippets would represent each action you need to take on data. They would be stored as raw text in a database or some other location where they could easily be altered. The code snippets would be compiled and then applied to the data at the time of execution. This would allow you to have a database full of different code snippets that you easily could retrieve, modify, and apply without having to alter your fundamental application each time.

It’s a rather complex scenario, but it should help you understand some of the possibilities. Now, look at how to make it happen.

CodeCompileUnit

To dynamically compile a class, start with a CodeCompileUnit from the System.CodeDom namespace. The CodeCompileUnit contains a program graph. To build up the code, you create a number of supporting objects and add them to the CodeCompileUnit instance. The objects represent common things you would have in your code if you were to build it at design time:

  • CodeNamespace—Represents the assigned namespace
  • CodeTypeDeclaration—Represents the type declaration
  • CodeMemberMethod—Represents a method

A HelloWorld Example

You can use the following sample code to generate code that contains a SayHello method that accepts a single parameter and returns a value. The value for the scriptBody method parameter becomes the body for the SayHello method. You’ll contain your code to create the CodeCompileUnit within a static class that accepts parameters that influence the result:

public static CodeCompileUnit CreateExecutionClass(string typeNamespace,
                                                   string typeName,
                                                   string scriptBody)
{
   // Create the CodeCompileUnit to contain the code
   CodeCompileUnit ccu = new CodeCompileUnit();

   // Assign the desired namespace
   CodeNamespace cns = new CodeNamespace(typeNamespace);
   cns.Imports.Add(new CodeNamespaceImport("System"));
   ccu.Namespaces.Add(cns);

   // Create the new class declaration
   CodeTypeDeclaration parentClass = new CodeTypeDeclaration(typeName);
   cns.Types.Add(parentClass);

   // Create the SayHello method that takes a parameter and has a
   // string return
   CodeMemberMethod method = new CodeMemberMethod();
   method.Name = "SayHello";
   method.Attributes = MemberAttributes.Public;
   CodeParameterDeclarationExpression arg = new
      CodeParameterDeclarationExpression(typeof(string),
                                         "inputMessage");
   method.Parameters.Add(arg);
   method.ReturnType = new CodeTypeReference(typeof(string));

   // Add the desired code to the method body
   CodeSnippetStatement methodBody =
      new CodeSnippetStatement(scriptBody);
   method.Statements.Add(methodBody);
   parentClass.Members.Add(method);

   return ccu;
}

CodeProvider

Now that you’ve created a CodeCompileUnit containing your code snippet, use it to generate the full source code to be compiled into your dynamic assembly. The following static method first calls the method from the previous example and then uses the CSharpCodeProvider to generate the full code:

public static string GenerateCode(string typeNamespace,
                                  string typeName,
                                  string scriptBody)
{
   // Call our prior method to create the CodeCompileUnit
   CodeCompileUnit ccu = CreateExecutionClass(typeNamespace,
                                              typeName, scriptBody);

   CSharpCodeProvider provider = new CSharpCodeProvider();
   CodeGeneratorOptions options = new CodeGeneratorOptions();
   options.BlankLinesBetweenMembers = false;
   options.IndentString = "t";

   StringWriter sw = new StringWriter();
   try
   {
      provider.GenerateCodeFromCompileUnit(ccu, sw, options);
      sw.Flush();
   }
   finally
   {
      sw.Close();
   }

   return sw.GetStringBuilder().ToString();
}

As an example, calling the method GenerateCode with the input values of “CodeGuru.DynamicCode”, “ScriptType”, and “return inputMessage;” produces the following output:

//---------------------------------------------------------------
// <auto-generated>
//   This code was generated by a tool.
//   Runtime Version:2.0.50630.0
//
//   Changes to this file may cause incorrect behavior and will
//   be lost if the code is regenerated.
// </auto-generated>
//---------------------------------------------------------------

namespace CodeGuru.DynamicCode {
   using System;

   public class ScriptType {
      public virtual string SayHello(string inputMessage) {
         return inputMessage;
      }
   }
}

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read