插件场景如何实现程序集绑定重定向?

How to achieve assembly binding redirect in a plugin scenario?

我有一个我无法控制的 plugin P 扩展和 application A (.NET40)。
P 程序集 (.NET40) 有一个 shared dependency D (.NET35)。

P和D都依赖FSharp.Core,但版本不同:

P 是针对 FSharp.Core, Version=4.4.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
编译的 D 是针对 FSharp.Core, Version=2.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

编译的

仅部署了 FSharp.Core,版本 =4.4.0.0,我订阅了 AppDomain.AssemblyResolve 以加载已部署的程序集。

当我在 GAC 中安装了两个 FSharp.Core 版本的机器上进行测试时,它们最终都加载了插件。

我的理解是绑定重定向将是这里的解决方案,但是如果不访问 app.config 怎么能做到呢?

您可以将 D 部署为 publisher policy 程序集。

这种方法的好处是客户端目录不需要包含 *.config 文件即可重定向到较新的版本。发布者策略允许程序集的发布者将 *.config 文件的二进制版本安装到 GAC(与程序集一起)。这样,CLR 将能够在 GAC 级别执行请求的重定向。

如果您想绕过某个应用的发布者政策,您可以在应用的 *.config 文件中使用 <publisherPolicy> 元素指定。

<?xml version="1.0" encoding="utf-8" ?>
<configuration> 
    <runtime>
        <assemblyBinding xmlns=“urn:schemas-microsoft-com:asm.v1”> 
            <publisherPolicy apply="no" />
        </assemblyBinding>
    </runtime> 
</configuration>

是的,的确如此。如果您正在编写插件,则 app.config 文件对于重定向程序集毫无用处。该插件将首先查找用户计算机上的 machine.config 文件,然后查找主程序的 *.config 文件.

下面是我在写SDL Trados 2017插件时,在插件场景下进行程序集绑定重定向的两步流程--

第一步:在插件本身中使用 try-catch 语句来发现有关加载失败的程序集的信息.
就我而言,我怀疑创建 Google Cloud AutoML 客户端所需的少数程序集之一是罪魁祸首,因此我在插件首次尝试创建 [=48] 的地方放置了一个 try-catch 语句=] Cloud AutoML 客户端:

    try
    {
        client = AutoMlClient.Create(); 
    }
    catch (Exception err)
    {
        using (System.IO.StreamWriter file = new System.IO.StreamWriter("C:/Desktop/log.txt", true))
        {
            file.WriteLine(err.ToString());
        }
    }

查看出错时创建的"log.txt"文件,发现如下信息:

    System.IO.FileNotFoundException: Could not load file or assembly 'Google.Apis.Auth, Version=1.41.1.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab' or one of its dependencies. The system cannot find the file specified.

所以!很明显,在创建 AutoML 客户端的过程中,该插件正在尝试 find.Google.Apis.Auth 版本 1.41.1.0。但是,为了让我的插件正确编译,我必须使用 NuGet 安装 Google.Apis.Auth 版本 1.42.0.0。因此需要程序集绑定重定向。

第二步:添加与该特定程序集相关的事件处理程序,这将在加载它之前更改其版本/public 密钥令牌信息。 在程序的最开始--初始化插件的主窗体window的地方,我添加了这段代码:

    public Main_form()
    {
        InitializeComponent();

        Version V14200 = new Version("1.42.0.0");
        RedirectAssembly("Google.Apis.Auth", V14200, "4b01fa6e34db77ab");
    }

    public static void RedirectAssembly(string assembly_name, Version targetVersion, string publicKeyToken)
    {
        ResolveEventHandler handler = null;

        handler = (sender, args) => {
            //gets the name of the assembly being requested by the plugin
            var requestedAssembly = new AssemblyName(args.Name);

            //if it is not the assembly we are trying to redirect, return null
            if (requestedAssembly.Name != assembly_name)
                return null;

            //if it IS the assembly we are trying to redirect, change it's version and public key token information
            requestedAssembly.Version = targetVersion;
            requestedAssembly.SetPublicKeyToken(new AssemblyName("x, PublicKeyToken=" + publicKeyToken).GetPublicKeyToken());
            requestedAssembly.CultureInfo = CultureInfo.InvariantCulture;

            //finally, load the assembly
            return Assembly.Load(requestedAssembly);
        };
        AppDomain.CurrentDomain.AssemblyResolve += handler;
    }

所以基本上,您必须从插件(使用 try-catch 语句)中获取有关哪个程序集加载失败的信息。然后,您必须添加一个事件处理程序,该处理程序将在相关程序集开始加载时生效。

就我而言,我知道 Google.Apis.Auth 是问题所在——插件想要加载版本 1.41.1.0,但我的插件包含版本 1.42.0.0。当插件开始寻找 Google.Apis.Auth (1.41.1.0) 时,事件处理程序介入并更改版本号,因此插件加载版本 1.42.0.0.

从未接受过任何计算机科学或编程方面的正式培训,我不知道 robust/recommendable 这个解决方案如何,但它对我有用。