using System; using System.CodeDom.Compiler; using System.Reflection; using System.Text; using System.Text.RegularExpressions; namespace SubSonic { /// /// Runs and executes code for use with our scripted bits - like Migrations /// public class CodeRunner { /// /// Executes the passed-in code /// /// ICodeLanguage /// the code /// the entry point method /// the params public static void RunAndExecute(ICodeLanguage lang, string sourceCode, string methodName, object[] parameters) { string[] source = new string[1]; source[0] = sourceCode; CompilerParameters compileParams = new CompilerParameters(); compileParams.GenerateInMemory = true; //Gonna create an assembly on the fly //so we need all the standard DLL's referenced from the GAC compileParams.ReferencedAssemblies.Add("System.Configuration.dll"); compileParams.ReferencedAssemblies.Add("System.Web.dll"); compileParams.ReferencedAssemblies.Add("System.Data.dll"); compileParams.ReferencedAssemblies.Add("System.dll"); compileParams.ReferencedAssemblies.Add("System.Xml.dll"); //add a dash of COM interop compileParams.ReferencedAssemblies.Add("mscorlib.dll"); //have to make sure SubSonic is added in. Since this assembly is //generated in memory it needs to be told exactly where the SubSonic.dll is //because SubSonic.dll doesn't live in the Framework folder, so lets grab //the same SubSonic.dll that is already loaded and use that one. Not //sure how much of a hack this is but it works for me. // //this is a little sketch because if the SubSonic.dll hasn't been //loaded yet then this will fail. Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); Assembly subsonicAssembly = Array.Find( assemblies, delegate(Assembly a) { AssemblyName an = a.GetName(); return an.Name == "SubSonic"; } ); if(subsonicAssembly == null) throw new Exception("Unable to location the SubSonic.dll. Make sure it's referenced in your project."); Uri subsonicUri = new Uri(subsonicAssembly.CodeBase); compileParams.ReferencedAssemblies.Add(subsonicUri.LocalPath); //compile the assembly CompilerResults results = lang.CreateCodeProvider().CompileAssemblyFromSource(compileParams, source); if(results.Errors.Count > 0 || results.CompiledAssembly == null) { if(results.Errors.Count > 0) { StringBuilder sbError = new StringBuilder(); foreach(CompilerError error in results.Errors) sbError.AppendLine(error.ErrorText); //fails throw new Exception("Compile errors: \r\n" + sbError); } if(results.CompiledAssembly == null) throw new Exception("Compiler errors: the code won't compile"); return; } string typeName = GetTypeName(sourceCode, lang); //instance up the class object instance = results.CompiledAssembly.CreateInstance(typeName); //if the instance is null, it means we haven't parsed the namespace/classname properly if(instance == null) throw new InvalidOperationException("SubSonic was not able to parse the namespace/class name of your Migration class properly - cannot find it: " + typeName); Type instanceType = instance.GetType(); //grab the method we're looking for MethodInfo method = instanceType.GetMethod(methodName); method.Invoke(instance, parameters); } /// /// Gets the name of the type. /// /// The source. /// The lang. /// private static string GetTypeName(string source, ICodeLanguage lang) { string classReplacement; string namespaceReplacement; Regex classRegex; Regex namespaceRegex; if (lang is VBCodeLanguage) { classReplacement = "${Class}"; namespaceReplacement = "${Namespace}"; classRegex = new Regex(@"Class (?\w*)"); namespaceRegex = new Regex(@"Namespace (?[a-zA-Z0-9.-[{]]*)"); } else { classReplacement = "${class}"; namespaceReplacement = "${namespace}"; classRegex = new Regex(@"class (?\w*)"); //many thanks to rballonline!!! namespaceRegex = new Regex(@"namespace (?[a-zA-Z0-9.-[{]]*)"); } string result = String.Empty; const string resultFormat = "{0}.{1}"; Match namespaceMatch = namespaceRegex.Match(source); Match classMatch = classRegex.Match(source); if(classMatch.Success && namespaceMatch.Success) result = string.Format(resultFormat, namespaceMatch.Result(namespaceReplacement), classMatch.Result(classReplacement)); return result; } } }