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

6

我有一个扩展了应用程序A(.NET40)的插件P,但我无法控制它。

P程序集(.NET40)具有一个共享依赖项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, Version=4.4.0.0"被部署,并且我订阅了AppDomain.AssemblyResolve事件以加载已部署的程序集。

当我在我的机器上进行测试时,两个FSharp.Core版本都被加载到插件中。

我的理解是,在没有访问app.config的情况下,绑定重定向将是解决方案。但该如何实现?


“共享依赖”是什么意思?D在哪里可以找到? - Funk
我只是想说,D是我的另一个项目(代码库的一部分),被我的其他项目引用。让D目标.NET40并引用相同的FSharp.Core版本将防止版本冲突。但是我需要D至少针对另一个项目的.NET35。 - oli
2个回答

4

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

这是我在为SDL Trados 2017编写插件时,在插件场景下执行程序集绑定重定向的两步过程--

第一步:在插件本身中使用try-catch语句发现无法加载程序集的信息
在我的情况下,我怀疑其中一个创建Google Cloud AutoML客户端所需的少数几个程序集有问题,因此我在插件首次尝试创建Google Cloud AutoML客户端的地方放置了try-catch语句:

    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客户端的过程中,该插件正在尝试查找Google.Apis.Auth版本1.41.1.0。但是,为了使我的插件正确编译,我不得不使用NuGet安装Google.Apis.Auth版本1.42.0.0。因此需要进行程序集绑定重定向。

步骤二:添加一个与特定程序集相关的事件处理程序,在加载它之前更改版本/公钥令牌信息。 在程序的开头 - 插件的主窗体窗口初始化时,我添加了这段代码:

    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。

由于我没有接受过任何计算机科学或编程方面的正式培训,因此我不知道这种解决方案有多健壮/可推荐,但对我来说有效。


3
您可以将D部署为发布者策略程序集。
这种方法的好处是,客户端目录不需要包含*.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>

3
不使用全局程序集缓存,有没有解决这个问题的方法? - Harry13

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