简体   繁体   English

如何使用 CodeDOM 在 AppDomain 中创建和加载程序集?

[英]How can I use CodeDOM to create and load an assembly in an AppDomain?

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.我正在开发一个项目,该项目将使用 CodeDOM 创建一个评估用户定义表达式的类,为该类创建一个程序集,并加载该程序集。 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.由于可以有相当数量的用户定义表达式,我想首先创建一个 AppDomain,为该 AppDomain 中的程序集执行 CodeDOM 创建/加载和执行,然后卸载 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.我搜索了不少,并且发现了如何加载现有装配成一个AppDomain的例子很多,但我似乎无法找到一个节目我如何从内部的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.此示例 ( DynamicCode ) 使用 CodeDOM 创建一个程序集,然后将其加载到 AppDomain 中,但是,作者将程序集生成到磁盘。 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). (即使这确实在临时文件夹中创建了一个 .dll)。

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.现在我需要做的就是将它包装在它自己的 AppDomain 中。

Chicken and egg problem.鸡和蛋的问题。 You need a little bootstrapper assembly that you can load in the new AppDomain.您需要一个小的引导程序程序集,您可以将其加载到新的 AppDomain 中。 With a well-known class that loads the CodeDom generated assembly and gets it going.使用一个众所周知的类加载 CodeDom 生成的程序集并使其运行。

Doing this with GenerateInMemory is pretty pointless, you would have to serialize that into the new AppDomain.使用 GenerateInMemory 执行此操作毫无意义,您必须将其序列化到新的 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.加载它会非常快,因为它实际上不必从磁盘上读取。

I found the answer that I was looking for at http://www.softwareinteractions.com/blog/2010/2/7/loading-and-unloading-net-assemblies.html .我在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.他有一篇很好的文章,详细介绍了 AppDomain 的创建和作为插件加载程序集。 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.我按照他的例子,能够创建一个 AppDomain,为我的 ExpressionEvaluator 类工厂创建一个代理,并成功调用它并接收结果。

Just use AssemblyBuilderAccess.Run when you define the dynamic assembly http://msdn.microsoft.com/en-us/library/system.reflection.emit.assemblybuilderaccess.aspx定义动态程序集时只需使用AssemblyBuilderAccess.Run http://msdn.microsoft.com/en-us/library/system.reflection.emit.assemblybuilderaccess.aspx

The dynamic assembly can be executed, but not saved.可以执行动态程序集,但不能保存。

Where does the CompilCode method come from in all of this? CompilCode方法从何而来? It seems to be that is the most important part, and you decide to leave it out?这似乎是最重要的部分,而您决定将其排除在外?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM