In this section I would like to demonstrate how to compile a .NET code on the fly. The code has been written in C#. It contains a class "InlineParser" which does the main job. It mainly, defines the class, does in-memory compilation and exposes a method for the execution of an inner method defined in the class. It also supports a choice of language whether C# or VB.NET for compilation.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.CSharp; using Microsoft.VisualBasic; using System.CodeDom.Compiler; using System.Reflection; using System.Xml.Linq; namespace com.eforceglobal.crux.bre { internal class InlineParser { string expression = string.Empty; string functionArguments = string.Empty; string language = "CSharp"; XDocument paramList = null; object objBase = null; public InlineParser(string expr, string functionArgs, XDocument parameters, string lang) { expression = expr; functionArguments = functionArgs; paramList = parameters; language = lang; } public bool init() { if (language.ToLower().Equals("csharp")) return InitCSharp(); else return InitVB(); } internal bool InitCSharp() { // Compile the expression in a class on the fly CSharpCodeProvider cp = new CSharpCodeProvider( new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } }); //ICodeCompiler ic = cp.CreateCompiler(); CompilerParameters cparam = new CompilerParameters(); cparam.GenerateInMemory = true; cparam.GenerateExecutable = false; // Reference Assembly cparam.ReferencedAssemblies.Add("system.dll"); cparam.ReferencedAssemblies.Add("mscorlib.dll"); cparam.ReferencedAssemblies.Add("System.Core.dll"); cparam.ReferencedAssemblies.Add("System.Xml.dll"); cparam.ReferencedAssemblies.Add("System.Xml.Linq.dll"); // Write your code StringBuilder sb = new StringBuilder(); sb.Append("using System;\n"); sb.Append("using System.Collections;\n"); sb.Append("using System.Collections.Generic;\n"); sb.Append("using System.Text;\n"); sb.Append("using System.Text.RegularExpressions;\n"); sb.Append("using System.Reflection;\n"); sb.Append("using System.Linq;\n"); sb.Append("using System.Xml.Linq;\n"); sb.Append("namespace com.eforceglobal.crux.bre {\n"); sb.Append("public class EvalClass {\n"); sb.Append("public EvalClass(){}\n"); sb.Append("public object Evaluate(\n"); sb.Append(functionArguments).Append(" ) {\n"); sb.Append(expression).Append("\n"); sb.Append("}\n}\n}"); string code = sb.ToString(); CompilerResults cres = cp.CompileAssemblyFromSource(cparam, code); // Compilation Unsuccessfull StringBuilder errors = new StringBuilder(); foreach (CompilerError cerr in cres.Errors) errors.Append(cerr.ErrorText); if (cres.Errors.Count > 0) throw new Exception(errors.ToString()); // Compilation Successfull if (cres.Errors.Count == 0 && cres.CompiledAssembly != null) { Type ClsObj = cres.CompiledAssembly.GetType("com.eforceglobal.crux.bre.EvalClass"); try { if (ClsObj != null) { objBase = Activator.CreateInstance(ClsObj); } } catch (Exception ex) { throw; } return true; } else return false; } internal bool InitVB() { // Compile the expression in a class on the fly VBCodeProvider vb = new VBCodeProvider( new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } }); //ICodeCompiler ic = cp.CreateCompiler(); CompilerParameters cparam = new CompilerParameters(); cparam.GenerateInMemory = true; cparam.GenerateExecutable = false; // Reference Assembly cparam.ReferencedAssemblies.Add("system.dll"); cparam.ReferencedAssemblies.Add("mscorlib.dll"); cparam.ReferencedAssemblies.Add("System.Core.dll"); cparam.ReferencedAssemblies.Add("System.Xml.dll"); cparam.ReferencedAssemblies.Add("System.Xml.Linq.dll"); // Write your code StringBuilder sb = new StringBuilder(); sb.Append("Imports System\n"); sb.Append("Imports System.Collections\n"); sb.Append("Imports System.Collections.Generic\n"); sb.Append("Imports System.Text\n"); sb.Append("Imports System.Text.RegularExpressions\n"); sb.Append("Imports System.Reflection\n"); sb.Append("Imports System.Linq\n"); sb.Append("Imports System.Xml.Linq\n"); sb.Append("Namespace com.eforceglobal.crux.bre \n"); sb.Append("Public Class EvalClass \n"); sb.Append("Public Function Evaluate ( "); sb.Append(functionArguments).Append(" ) As Object\n"); sb.Append(expression).Append("\n"); sb.Append("End Function\n"); sb.Append("End Class\n"); sb.Append("End Namespace"); string code = sb.ToString(); CompilerResults cres = vb.CompileAssemblyFromSource(cparam, code); // Compilation Unsuccessfull StringBuilder errors = new StringBuilder(); foreach (CompilerError cerr in cres.Errors) errors.Append(cerr.ErrorText); if (cres.Errors.Count > 0) throw new Exception(errors.ToString()); // Compilation Successfull if (cres.Errors.Count == 0 && cres.CompiledAssembly != null) { Type ClsObj = cres.CompiledAssembly.GetType("com.eforceglobal.crux.bre.EvalClass"); try { if (ClsObj != null) { objBase = Activator.CreateInstance(ClsObj); } } catch (Exception ex) { throw; } return true; } else return false; } public string Evaluate() { string result = string.Empty; Type type = objBase.GetType(); MethodInfo mInfo = type.GetMethod("Evaluate"); if (mInfo != null) { ParameterInfo[] pInfo = mInfo.GetParameters(); object[] arguments = null; if (pInfo != null) arguments = new object[pInfo.Length]; if (pInfo != null) { foreach (ParameterInfo p in pInfo) if (paramList.Element("arguments").Elements(p.Name) != null) { arguments[p.Position] = (paramList.Element("arguments") .Elements(p.Name).Single().Value); } if (pInfo.Length != arguments.Length) throw new ArgumentException("Insufficient parameters."); } result = mInfo.Invoke(objBase, arguments).ToString(); } return result; } } }
Notice that the class name of the class to be compiled is "EvalClass" and the inner method of the class is "Evaluate". Calling the "Evaluate()" method requires you to execute the following lines of code.
var parser = new InlineParser(subExpression, funcArgs, Arguments.Document, lang); parser.init(); retString = parser.Evaluate();
Enjoy!!
Manish