Sunday, 8 April 2012

Dynamically compiling code in memory using the CodeDomProvider

Recently I had to dynamically load some code from a text file and execute it at run time in memory. The CodeDomProvider seemed to do the trick nicely as you can see below. You can generate an assembly in memory using the CompileAssemblyFromSource method like I have done or use it's CompileAssemblyFromFile or CompileAssemblyFromDom counterparts.
using System;
using System.Collections.Generic;
using System.Linq;
 
using Microsoft.CSharp;
using System.CodeDom.Compiler;
 
namespace DynamicCompilerConsoleApp
{
    
    public interface IConsoleWriter
    {
        void WriteToConsole(string message);
    }
    
    public class DynamicAssembyExecution
    {
        public static void DynamicCodeExecution()
        {
            const string code = @"using System;
                                  using DynamicCompilerConsoleApp;
                            namespace LoadXmlTestConsole
                            {
                                public class ConsoleWriter : IConsoleWriter
                                {
                                    public void WriteToConsole(string message)
                                    {
                                        Console.WriteLine(message);
                                    }
                                }
                            }";
 
            CodeDomProvider codeCompiler =
                new CSharpCodeProvider(new Dictionary<stringstring> {{"CompilerVersion""v4.0"}});
            // Create the optional compiler parameters
            CompilerParameters compilerParameters = new CompilerParameters
                                                        {
                                                            GenerateExecutable = false,
                                                            GenerateInMemory = true,
                                                            WarningLevel = 3,
                                                            TreatWarningsAsErrors = false,
                                                            CompilerOptions = "/optimize"
                                                        };
 
            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                compilerParameters.ReferencedAssemblies.Add(assembly.Location);    
            }
 
            // Compile the string containing the code, using the provided set of parameters
            CompilerResults compilerResults = codeCompiler.CompileAssemblyFromSource(compilerParameters,
                                                                                        code);
            //Display any compiler errors in the console window
            if (compilerResults.Errors.HasErrors)
                foreach (string line in compilerResults.Output)
                    Console.WriteLine(line);
 
            // Get the required type out of the assembly
            Type consoleWriterType =
                compilerResults.CompiledAssembly.GetTypes().SingleOrDefault(
                    lt => lt.GetInterface(typeof (IConsoleWriter).Name) != null);
              
            // create an instance of the type
            IConsoleWriter consoleWriter = (IConsoleWriter)Activator.CreateInstance(consoleWriterType);
                
            // execute the compiled code
            consoleWriter.WriteToConsole("hello world");
            
        }
    }
}