【问题标题】:CSharpCodeProvider and resolution of dynamic assembliesCSharpCodeProvider 和动态程序集的解析
【发布时间】:2012-10-27 16:41:21
【问题描述】:

我有一个应用程序,我在其中使用 CSharpCodeProvider 来允许通过 C# 代码进行集成脚本。脚本可以引用动态发出的程序集(这些是通过 IL Emit 创建的);为了允许这个引用,我将动态生成的程序集保存到磁盘上的一个文件中,然后将此位置添加到 CompilerParameters.ReferencedAssemblies 集合中。

这适用于脚本的第一次编译和执行。我有实例化从脚本生成的类的代码,该类有一个带参数的构造函数,其中一些是来自动态发出的程序集的类型。它看起来像这样:

        var hostType = _compilerResults.CompiledAssembly.GetType("ExMod.Engine.ScriptHost");

        var parameters = new List<object>();
        parameters.Add(simulation);

        var extraParameters =
            simulation.Environment.Controllers.
            Select(c => c.GetPrecompiledContext()).
            Where(c => c != null);
        parameters.AddRange(extraParameters);

        _scriptHost = (ScriptHostBase)Activator.CreateInstance(hostType, parameters.ToArray());

extraParameters 表达式是添加对外部动态生成类型的引用的地方。

当我尝试第二次编译和运行脚本时出现问题。仅当我还重新发出了新版本的 IL Emit 程序集时才会发生这种情况。该异常发生在 Activator.CreateInstance 调用上,并且是:

MissingMethodException: Constructor on type 'ExMod.Engine.ScriptHost' not found.

观察:

  1. 我已通过调试器确认确实存在具有正确参数的构造函数。
  2. 我还确认(使用调试器中的 Make Object ID)导致问题是因为构造函数中的“Controller”参数(从动态发出的程序集中引用的类型)指的是原始动态程序集,而不是比最近发布的动态程序集,这就是类型不匹配且构造函数无法解析的原因。
  3. 当我运行 CSharpCodeProvider 编译器然后尝试运行脚本时,它从不调用我的 AppDomain AssemblyResolve 事件,这是我有特殊逻辑始终解析为“最新”动态程序集的地方。

我的理论是,CSharpCodeProvider 以某种方式解决了“最早”的动态程序集,而不是“最新的”。发生这种情况是因为它们都具有相同的名称和位置。问题:如何让 CSharpCodeProvider 解析为最新版本的动态程序集?

我考虑过更改每个动态发出的程序集的位置,但我不希望为每个脚本迭代在磁盘上创建一堆副本。

【问题讨论】:

  • CompiledAssembly 使用 Assembly.Load() 加载程序集。你真的应该考虑使用 AppDomain,这样你就可以再次卸载这些程序集。
  • @Hans,我同意,使用 AppDomain 是更好的解决方案。现在,我每次构建一个中等大小的项目(发出新程序集)时都会泄漏大约 23MB,而且甚至没有运行任何脚本。如果源代码没有更改,我可以通过缓存程序集来缓解其中的一些问题,但是在 AppDomain 中运行整个模拟肯定会更好(甚至可能是外部托管进程。)这些问题可能会将其推向更高的优先级列表。

标签: c# .net dynamic assemblies code-generation


【解决方案1】:

我发现我可以通过让 Emit 代码为每个构建创建一个新的构建号来解决这个问题。我只是在生成 AssemblyName 的代码中使用它:

        var name = new AssemblyName();
        name.Name = "AB-PLC";
        name.Version = new Version(0, 0, Interlocked.Increment(ref _buildNumber), 0);

上面的_buildNumber是一个简单的静态int字段。

这允许 CSharpCodeProvider 解析正确的程序集,因为现在不同构建的 FullName 不再匹配。我仍然只有一个导出到磁盘的程序集副本(被每个 IL 发射构建覆盖。)

我仍然很想知道是否可以让 CSharpCodeProvider 在不更改其 FullName 的情况下解析正确的程序集,但这个解决方案目前足以满足我的目的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多