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

7
我正在开发一个项目,使用CodeDOM创建一个类来评估用户定义的表达式,创建一个该类的程序集,并加载该程序集。由于可能有相当数量的用户定义的表达式,我想首先创建一个AppDomain,在该AppDomain中执行CodeDOM的创建/加载和程序集的执行,然后卸载AppDomain。
我已经搜索了很多,找到了许多将现有程序集加载到AppDomain中的示例,但似乎找不到一个显示如何从AppDomain内部创建程序集的示例。
这个例子(DynamicCode)使用CodeDOM创建一个程序集,然后将其加载到AppDomain中,但是,作者是将程序集生成到磁盘上的。我更喜欢在内存中生成程序集,这样我就不必管理生成的程序集的清理(即使这会在临时文件夹中创建一个.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中。

4个回答

3
鸡和蛋问题。您需要一个小的引导程序集,可以在新的AppDomain中加载。使用一个众所周知的类来加载CodeDom生成的程序集并启动它。
使用GenerateInMemory做这件事情是相当无意义的,因为您必须将其序列化到新的AppDomain中。那只是一堆开销,不如从磁盘加载它,因为它已经在那里了。而且它已经在内存中了。文件系统缓存的内存。由于它实际上不必从磁盘读取,所以加载速度非常快。

3

1
这是一个已失效的链接。 - David L
@David L:https://web.archive.org/web/20140704035449/http://www.softwareinteractions.com/blog/2010/2/7/loading-and-unloading-net-assemblies.html - Stefan Steiger
它们现在都是无效链接。 - mehdi.loa

1

1
我相信你把 Reflection.Emit 和 CodeDom 混淆了。 - Kirk Woll
谢谢Joel。我正在研究动态装配。 - Welton v3.62

0

在这一切中,CompilCode方法是从哪里来的?它似乎是最重要的部分,你决定将其省略了吗?


“CompileCode”方法并不是与问题相关的,问题是如何在运行时生成源代码、创建程序集并将其加载到“AppDomain”中。我意识到我的初始问题可能并没有完全准确地描述我的问题,但随着我收到反馈并进行更多的研究,它细化了问题的范围和定义。具体来说,我尝试执行的操作顺序。被接受答案中的文章澄清了我需要采取的方法,并帮助我解决了问题。 - Welton v3.62

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接