Dynamically creating C# code using the CodeDomProvider

I’ve been working on some code which generates C# classes. Basically it creates POCO classes to allow me to serialize/deserialize data from a data file into the POCO classes. The data file format can change and so this code allows me to easily regenerate the classes I need. This works fine, but I thought it might be better if I could create the code on the fly and use in without having to recompile the application (that would otherwise include the compiled source). In other words I wanted to create the C# classes and then generate the types and use them all dynamically at runtime.

.NET includes the CodeDomProvider class which can easily be used to create an assembly with our types created on the fly.

Here’s the code

CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");

CompilerParameters cp = new CompilerParameters();
cp.GenerateInMemory = true;
cp.TreatWarningsAsErrors = false;
cp.ReferencedAssemblies.Add("putridparrot.Data.dll");

CompilerResults cr = provider.CompileAssemblyFromSource(cp, source);

The above is pretty self explanatory, but let’s go through it anyway.

First off, we create a CodeDomeProvider specifying the provider we require for C# code. We can (currently) create code dom’s for the following languages

  1. CSharp – C#
  2. VisualBasic – VB.NET
  3. JScript – JavaScript
  4. Cpp – C++

Next we create the compiler parameters. In this instance we want the code generated in memory, as opposed to using a file and I don’t want “warnings as error’s” in this instance. I do however need to add any of my referenced assemblies. In this case the code uses one external assembly, the putridparrot.Data.dll (as the generated code uses attributes from this assembly to mark the properties of the POCO for serialization/deserialization).

Finally we compile the code into the assembly from the source code supplied (in this case the string source contains the C# class to be compiled). The CompilerResults may include errors, so we can check this by using the following

cr.Errors.Count

Assuming we’ve no errors, then we can now access a compiled type by interacting with the assembly created by the CodeDomProvider, for example

Type type = cr.CompiledAssembly.GetType("AutoGeneratedDataType");

The CompiledAssembly is just a standard Assembly at this point, so obviously you can do all the standard things you can do on any other Assembly object.

Side note

As a side note we can iterate over the languages supported via the CodeDomProvider using the following

CompilerInfo[] compilerInfo = CodeDomProvider.GetAllCompilerInfo();
foreach (var ci in compilerInfo)
{
   string[] languages = ci.GetLanguages();
   foreach(var language in languages)
   {
      Console.WriteLine(language);
   }
}