You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通过MEF动态加载.NET插件时,如何使其binding redirect生效?

问题描述

我有一个.NET 4.8类库,通过MEF作为插件加载。该类库的部分依赖项自动生成了binding redirect,配置文件MyLibrary.dll.config内容如下:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
      </dependentAssembly>
    </assemblyBinding>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
      </dependentAssembly>
    </assemblyBinding>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Threading.Channels" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

我的.NET 4.8主应用通过MEF动态加载该类库(如MyLibrary.dll)时,忽略了类库旁的MyLibrary.dll.config中的binding redirect,导致程序集加载失败并抛出CompositionException:

Unhandled Exception: System.ComponentModel.Composition.CompositionException: The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.
1) Could not load file or assembly 'Microsoft.Bcl.AsyncInterfaces, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
Resulting in: An exception occurred while trying to create an instance of type 'MyLibrary.MyType'.
Resulting in: Cannot activate part 'MyLibrary.MyType'.
Element: MyLibrary.MyType -->  MyLibrary.MyType -->  AssemblyCatalog (Assembly="MyLibrary, Version=3.9.0.0, Culture=neutral, PublicKeyToken=null")
Resulting in: Cannot get export 'MyLibrary.MyType (ContractName="LibraryBaseType")' from part 'MyLibrary.MyType'.
Element: MyLibrary.MyType (ContractName="LibraryBaseType") -->  MyLibrary.MyType -->  AssemblyCatalog (Assembly="MyLibrary, Version=3.9.0.0, Culture=neutral, PublicKeyToken=null")
   at System.ComponentModel.Composition.Hosting.CompositionServices.GetExportedValueFromComposedPart(ImportEngine engine, ComposablePart part, ExportDefinition definition)
   at System.ComponentModel.Composition.Hosting.CatalogExportProvider.GetExportedValue(CatalogPart part, ExportDefinition export, Boolean isSharedPart)
   at System.ComponentModel.Composition.Primitives.Export.get_Value()
   at System.ComponentModel.Composition.ExportServices.GetCastedExportedValue[T](Export export)
   at System.Lazy`1.CreateValue()
   at System.Lazy`1.LazyInitValue()
   at MainApplication.Start(String[] args) in ...\MainApplication.cs:line 65
   at MainApplication.Main(String[] args) in ...\MainApplication.cs:line 31

请问是否有办法让主应用识别动态加载类库旁的binding redirect?还是只能将所有binding redirect复制到主应用的app.config中?

解决方法

方法1:合并配置到主应用app.config(最直接可靠)

这是.NET Framework下最稳妥的方案,直接把插件MyLibrary.dll.config里<runtime>节点下的所有<assemblyBinding>配置,复制到主应用app.config对应的<runtime>节点中。主应用启动时会加载这些全局重定向规则,MEF加载插件时就能自动应用。

方法2:手动加载插件配置并合并到主应用

可以在加载MEF插件前,通过代码读取插件的配置文件,将其中的binding redirect合并到当前应用的配置中:

using System.Configuration;
using System.Xml;
using System.IO;
using System.Reflection;

// 先获取插件所在目录
string pluginAssemblyPath = typeof(MyLibrary.MyType).Assembly.Location;
string pluginConfigPath = Path.Combine(Path.GetDirectoryName(pluginAssemblyPath), "MyLibrary.dll.config");

// 加载插件配置
ExeConfigurationFileMap configMap = new ExeConfigurationFileMap { ExeConfigFilename = pluginConfigPath };
Configuration pluginConfig = ConfigurationManager.OpenMappedExeConfiguration(configMap, ConfigurationUserLevel.None);

// 获取插件的runtime配置节点
ConfigurationSection pluginRuntimeSection = pluginConfig.GetSection("runtime");
if (pluginRuntimeSection != null)
{
    XmlDocument pluginXml = new XmlDocument();
    pluginXml.LoadXml(pluginRuntimeSection.SectionInformation.GetRawXml());

    // 获取主应用的runtime配置节点
    Configuration mainConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
    ConfigurationSection mainRuntimeSection = mainConfig.GetSection("runtime");
    if (mainRuntimeSection != null)
    {
        XmlDocument mainXml = new XmlDocument();
        mainXml.LoadXml(mainRuntimeSection.SectionInformation.GetRawXml());

        // 合并插件的assemblyBinding节点到主应用配置
        foreach (XmlNode assemblyBindingNode in pluginXml.SelectNodes("//assemblyBinding"))
        {
            XmlNode importedNode = mainXml.ImportNode(assemblyBindingNode, true);
            mainXml.DocumentElement.AppendChild(importedNode);
        }

        // 更新主应用配置并刷新
        mainRuntimeSection.SectionInformation.SetRawXml(mainXml.OuterXml);
        mainConfig.Save(ConfigurationSaveMode.Modified);
        ConfigurationManager.RefreshSection("runtime");
    }
}

注意:这段代码必须在初始化MEF目录/程序集之前执行,确保重定向规则提前生效。

方法3:通过AssemblyResolve事件手动处理加载

如果不想修改配置文件,可以订阅AppDomain.CurrentDomain.AssemblyResolve事件,在程序集加载失败时手动指定正确版本的依赖路径:

using System.Reflection;
using System.IO;

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
    AssemblyName requestedAssembly = new AssemblyName(args.Name);
    string pluginDir = Path.GetDirectoryName(typeof(MyLibrary.MyType).Assembly.Location);

    // 针对每个需要重定向的依赖单独处理
    switch (requestedAssembly.Name)
    {
        case "Microsoft.Bcl.AsyncInterfaces":
            return Assembly.LoadFrom(Path.Combine(pluginDir, "Microsoft.Bcl.AsyncInterfaces.dll"));
        case "System.Buffers":
            return Assembly.LoadFrom(Path.Combine(pluginDir, "System.Buffers.dll"));
        case "System.Memory":
            return Assembly.LoadFrom(Path.Combine(pluginDir, "System.Memory.dll"));
        // 其他依赖依次添加
        default:
            return null;
    }
};

这种方式需要逐个处理每个依赖,适合依赖数量较少的场景,灵活性高但维护成本也相对较高。


内容的提问来源于stack exchange,提问作者Brian Henry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 13:40:44