using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; namespace CodeCompiler { public class CompilationException : Exception { public List<CompilerError> Errors { get; private set; } public CompilationException(CompilerErrorCollection errors) : base("Error compiling expression") { Errors = new List<CompilerError>(); foreach (CompilerError error in errors) Errors.Add(error); } } public class Compiler { public List<string> ReferenceDLLs { get; private set; } public List<Assembly> ReferenceAssemblies { get; private set; } public Compiler() { ReferenceDLLs = new List<string> { "System.dll", "System.Core.dll", "System.Data.dll", "System.Data.DataSetExtensions.dll", "System.Xml.dll", "System.Xml.Linq.dll" }; //ReferenceDLLs = AppDomain.CurrentDomain.GetAssemblies().Where(x => !x.IsDynamic).Select(x => x.Location).ToList(); ReferenceAssemblies = new List<Assembly>(); } public Assembly CompileCSFile(string csFilePath) { using (StreamReader reader = new StreamReader(csFilePath)) return compileString(reader.ReadToEnd()); } private MethodInfo compileStringToMethodInfo(string code) { Assembly assembly = compileString(code); return assembly.GetType("FakeNamespace.FakeClass").GetMethod("MethodResult", BindingFlags.Static | BindingFlags.Public); } private Assembly compileString(string code) { CodeDomProvider compiler = CodeDomProvider.CreateProvider("CSharp"); CompilerResults result = compiler.CompileAssemblyFromSource(new CompilerParameters(ReferenceDLLs.Union(ReferenceAssemblies.ConvertAll(x => x.Location)).ToArray()), code); if (result.Errors.Count > 0) throw new CompilationException(result.Errors); return result.CompiledAssembly; } private List<string> defaultUsingStatements() { return new List<string>() { "using System;", "using System.Collections.Generic;", "using System.Linq;", "using System.IO;" }; } private string surroundLambaWithFakeClass<X, Y>(string text, List<string> usingStatements = null) { usingStatements = usingStatements ?? defaultUsingStatements(); string code = usingStatements.Aggregate((x, y) => x + "\r\n" + y) + @" namespace FakeNamespace { public class FakeClass { public static CLASS2 MethodResult(CLASS1 param1) { Func<CLASS1, CLASS2> lambda = " + text + @"; return lambda(param1); } } }"; code = code.Replace("CLASS1", typeof(X).ToString()).Replace("CLASS2", typeof(Y).ToString()).Replace("`1[", "<").Replace("]", ">"); return code; } private string surroundLambaWithFakeClass<X>(string text, List<string> usingStatements = null) { usingStatements = usingStatements ?? defaultUsingStatements(); string code = usingStatements.Aggregate((x, y) => x + "\r\n" + y) + @" namespace FakeNamespace { public class FakeClass { public static void MethodResult(CLASS1 param1) { Action<CLASS1> lambda = " + text + @"; return lambda(param1); } } }"; code = code.Replace("CLASS1", typeof(X).ToString()).Replace("`1[", "<").Replace("]", ">"); return code; } private string surroundMethodWithFakeClass<X>(string text, List<string> usingStatements = null) { usingStatements = usingStatements ?? defaultUsingStatements(); string code = usingStatements.Aggregate((x, y) => x + "\r\n" + y) + @" namespace FakeNamespace { public class FakeClass { public static void MethodResult(CLASS1 param1) { " + text + @" } } }"; code = code.Replace("CLASS1", typeof(X).ToString()).Replace("`1[", "<").Replace("]", ">"); return code; } private string surroundMethodWithFakeClass<X, Y>(string text, List<string> usingStatements = null) { usingStatements = usingStatements ?? defaultUsingStatements(); string code = usingStatements.Aggregate((x, y) => x + "\r\n" + y) + @" namespace FakeNamespace { public class FakeClass { public static CLASS2 MethodResult(CLASS1 param1) { " + text + @" } } }"; code = code.Replace("CLASS1", typeof(X).ToString()).Replace("CLASS2", typeof(Y).ToString()).Replace("`1[", "<").Replace("]", ">"); return code; } public Func<X, Y> ConvertMethodStringToMethodInfo<X, Y>(string text, List<string> usingStatements = null) { string code = surroundMethodWithFakeClass<X, Y>(text, usingStatements); MethodInfo method = compileStringToMethodInfo(code); return x => (Y)method.Invoke(null, new object[] { x }); } public Action<X> ConvertMethodStringToMethodInfo<X>(string text, List<string> usingStatements = null) { string code = surroundMethodWithFakeClass<X>(text, usingStatements); MethodInfo method = compileStringToMethodInfo(code); return x => method.Invoke(null, new object[] { x }); } public Func<X, Y> ConvertLambaStringToMethodInfo<X, Y>(string text, List<string> usingStatements = null) { string code = surroundLambaWithFakeClass<X, Y>(text, usingStatements); MethodInfo methodInfo = compileStringToMethodInfo(code); Func<X, Y> result = x => (Y)methodInfo.Invoke(null, new object[] { x }); return result; } public Action<X> ConvertLambaStringToMethodInfo<X>(string text, List<string> usingStatements = null) { string code = surroundLambaWithFakeClass<X>(text, usingStatements); MethodInfo methodInfo = compileStringToMethodInfo(code); return x => methodInfo.Invoke(null, new object[] { x }); } public static string EscapeToProtectFromCodeInjection(string searchText) { return "\"" + searchText.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\""; } } }
Here's how to use it:
Func<string, IEnumerable<string>> stringSplitFunction = Compiler.ConvertLambaStringToMethodInfo<String, IEnumerable<String>>("x => x.Split(new string[] { \",\" }, StringSplitOptions.RemoveEmptyEntries).AsEnumerable()"); IEnumerable<string> result = stringSplitFunction("val1,val2");