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

发布于 2024-09-14 13:00:16 字数 2676 浏览 2 评论 0原文

我正在开发一个项目,该项目将使用 CodeDOM 创建一个类,该类计算用户定义的表达式、为该类创建程序集并加载该程序集。由于可能有相当数量的用户定义表达式,我想首先创建一个 AppDomain,执行 CodeDOM 创建/加载并执行该 AppDomain 中的程序集,然后卸载 AppDomain。

我进行了相当多的搜索,发现了许多如何将现有程序集加载到 AppDomain 中的示例,但我似乎找不到一个向我展示如何从内部创建程序集的示例。应用程序域。

此示例 (DynamicCode) 使用 CodeDOM 创建程序集,然后加载它然而,作者正在将程序集生成到磁盘。我更愿意在内存中生成程序集,这样我就不必管理生成的程序集的清理。 (即使这确实在临时文件夹中创建了一个 .dll)。

谁能给我举个例子来说明如何做到这一点?

任何帮助将不胜感激。

我已经包含了我的代码的一些摘录,以便大家可以了解我到目前为止所拥有的内容:

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;
}

我相信这些代码片段显示了我的项目的基本功能。现在我需要做的就是将其包装在自己的 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. 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

情话墙 2024-09-21 13:00:16

我在 http://www.softwareinteractions.com/blog/2010/2/7/loading-and-unloading-net-assemblies.html。他有一篇很好的文章,详细介绍了 AppDomain 的创建以及将程序集加载为插件。我按照他的示例创建了一个 AppDomain,为我的 ExpressionEvaluator 类工厂创建了一个代理,并成功调用它并接收结果。

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.

心的憧憬 2024-09-21 13:00:16

先有鸡还是先有蛋的问题。您需要一个可以在新 AppDomain 中加载的小引导程序集。使用一个众所周知的类来加载 CodeDom 生成的程序集并使其运行。

使用GenerateInMemory 执行此操作毫无意义,您必须将其序列化到新的AppDomain 中。这只是一堆开销,不妨从磁盘加载它,无论如何它都在那里。而且它已经在记忆中了。文件系统缓存的内存。加载它会非常快,因为实际上不需要从磁盘上读取它。

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.

枯寂 2024-09-21 13:00:16

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.

待天淡蓝洁白时 2024-09-21 13:00:16

CompilCode 方法从何而来?看来这是最重要的部分,你决定忽略它?

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?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文