How can I use CodeDOM to create and load an assembly in an AppDomain?
Asked Answered
Z

4

7

I am working on a project that will use CodeDOM to create a class that evaluates a user-defined expression, creates an assembly for the class, and loads the assembly. Since there can be a fair number of user-defined expressions, I would like to first create an AppDomain, execute the CodeDOM creation/loading and executing for the assembly within that AppDomain, and then unload the AppDomain.

I've searched quite a bit, and found many examples of how to load an existing assembly into an AppDomain, but I can't seem to find one that shows me how to create the assembly from within the AppDomain.

This example (DynamicCode) creates an assembly using CodeDOM, and then loads it into an AppDomain, however, the author is generating the assembly to disk. I would prefer to generate the assembly in memory, so that I do not have to manage the cleanup of the generated assemblies. (even though this does create a .dll in a temp folder).

Can anyone point me to an example of how to do this?

Any help would be greatly appreciated.

I've included some excerpts from my code so you can all get a feel for what I have so far:

private string CreateSource()
{
    CodeCompileUnit codeUnit = new CodeCompileUnit();
    CodeNamespace codeNamespace = new CodeNamespace(Namespace);
    CodeTypeDeclaration codeClass = new CodeTypeDeclaration
    {
        Name = "ExpressionEvaluator",
        IsClass = true,
        TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed
    };

    codeNamespace.Types.Add(codeClass);
    codeUnit.Namespaces.Add(codeNamespace);

    AddMethods(codeClass);

    string result = GenerateSourceCode(codeUnit);

    return result.ToString();
}

private CompilerResults CompileSource(string source)
{
    using (CodeDomProvider provider = new CSharpCodeProvider())
    {
        CompilerParameters parameters = CreateCompilerParameters();
        CompilerResults result = CompileCode(provider, parameters, source);

        return result;
    }
}

private static CompilerParameters CreateCompilerParameters()
{
    CompilerParameters result = new CompilerParameters
    {
        CompilerOptions = "/target:library",
        GenerateExecutable = false,
        GenerateInMemory = true
    };

    result.ReferencedAssemblies.Add("System.dll");

    return result;
}

private object RunEvaluator(CompilerResults compilerResults)
{
    object result = null;
    Assembly assembly = compilerResults.CompiledAssembly;

    if (assembly != null)
    {
        string className = "ExpressionEvaluator";
        object instance = assembly.CreateInstance("Lab.ExpressionEvaluator");

        Module[] modules = assembly.GetModules(false);

        Type type = (from t in modules[0].GetTypes()
                     where t.Name == className
                     select t).FirstOrDefault();

        MethodInfo method = (from m in type.GetMethods()
                             where m.Name == "Evaluate"
                             select m).FirstOrDefault();

        result = method.Invoke(instance, null);
    }
    else
    {
        throw new Exception("Unable to load Evaluator assembly");
    }

    return result;
}

I believe these code snippets show the basic functionality of my project. Now all I need to do is wrap it in its own AppDomain.

Zeus answered 18/8, 2010 at 14:7 Comment(0)
Z
3

I found the answer that I was looking for at http://www.softwareinteractions.com/blog/2010/2/7/loading-and-unloading-net-assemblies.html. He has a nice article detailing the creation of an AppDomain and loading an assembly as a plugin. I followed his example and was able to create an AppDomain, create a proxy for my ExpressionEvaluator class factory and successfully call it and receive results.

Zeus answered 19/8, 2010 at 21:38 Comment(3)
This is now a dead link.Prescience
@David L: web.archive.org/web/20140704035449/http://…Obaza
They are both now dead links.Rodgers
S
3

Chicken and egg problem. You need a little bootstrapper assembly that you can load in the new AppDomain. With a well-known class that loads the CodeDom generated assembly and gets it going.

Doing this with GenerateInMemory is pretty pointless, you would have to serialize that into the new AppDomain. That's just a bunch of overhead, might as well load it from disk, it is there anyway. And it is already in memory. The file system cache's memory. Loading it will be very fast since it doesn't actually have to be read off the disk.

Shirleeshirleen answered 18/8, 2010 at 15:27 Comment(0)
Z
3

I found the answer that I was looking for at http://www.softwareinteractions.com/blog/2010/2/7/loading-and-unloading-net-assemblies.html. He has a nice article detailing the creation of an AppDomain and loading an assembly as a plugin. I followed his example and was able to create an AppDomain, create a proxy for my ExpressionEvaluator class factory and successfully call it and receive results.

Zeus answered 19/8, 2010 at 21:38 Comment(3)
This is now a dead link.Prescience
@David L: web.archive.org/web/20140704035449/http://…Obaza
They are both now dead links.Rodgers
G
1

Just use AssemblyBuilderAccess.Run when you define the dynamic assembly http://msdn.microsoft.com/en-us/library/system.reflection.emit.assemblybuilderaccess.aspx

The dynamic assembly can be executed, but not saved.

Godfry answered 18/8, 2010 at 14:13 Comment(2)
I believe you are confusing Reflection.Emit with CodeDom.Cocainize
Thanks Joel. I'm looking into the dynamic assembly.Zeus
T
0

Where does the CompilCode method come from in all of this? It seems to be that is the most important part, and you decide to leave it out?

Tortuosity answered 7/10, 2012 at 10:46 Comment(1)
The CompileCode method isn't really germane to the issue, which was how to generate source code, create an assembly, and load it into an AppDomain at run-time. I realize that my initial question may not have been an entirely accurate description of my problem, but as I recevied feedback and did more research, it refined the scope and definition of the issue. Specifically, the order of operations which I was attempting to perform. The article in the accepted answer clarified the approach I needed to take, and helped me resovle my issue.Zeus

© 2022 - 2024 — McMap. All rights reserved.