CSharpCodeProvider和动态程序集的解析

时间:2012-11-07 15:32:06

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

我有一个应用程序,我使用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组件的新版本时才会发生这种情况。 Exception发生在Activator.CreateInstance调用上,并且是:

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

观察:

  1. 我已通过调试器确认构造函数确实存在且参数正确。
  2. 我还确认(使用调试器中的Make Object ID)导致问题,因为构造函数中的'Controller'参数(来自动态发出的程序集的引用类型)是指原始动态程序集,而不是而不是最近发出的动态程序集,这就是类型不匹配且构造函数无法解析的原因。
  3. 当我运行CSharpCodeProvider编译器然后尝试运行脚本时,它从不调用我的AppDomain AssemblyResolve事件,这是我有特殊逻辑总是解析为'最新'动态程序集。
  4. 我的理论是CSharpCodeProvider以某种方式解析'最早'的动态集合而不是'最新'。发生这种情况是因为它们具有相同的名称和位置。问题:如何让CSharpCodeProvider解析为最新版本的动态程序集?

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

1 个答案:

答案 0 :(得分:0)

我发现我可以通过让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的情况下解析正确的程序集,但此解决方案目前已足够用于我的目的。