为什么我的程序集需要按特定顺序加载? [英] Why do my assemblies need to be loaded in a specific order?

查看:44
本文介绍了为什么我的程序集需要按特定顺序加载?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个简单的插件,偶然发现 contractType.IsAssignableFrom(pluginType) 会根据加载顺序返回不同的结果.

I am writing a simple plugin and stumbled upon contractType.IsAssignableFrom(pluginType) returning different results depending on the order of loading.

在插件上调用 IsAssignableFrom 会按预期返回True.
但是,如果我在加载插件之前 加载了Contract程序集,则插件上的 IsAssignableFrom 会返回False.

Calling IsAssignableFromon the Plugin returns True as expected.
But if I load the Contract assembly before loading the Plugin, IsAssignableFrom on the Plugin returns False.

我正在运行Win10和dotnet4.7,但我怀疑这是否有任何意义.

I am running Win10 and dotnet4.7 but I doubt that has any relevancy.

[TestMethod]
public void SimplyLoadingPlugin_Succeeds()
{
    var plugin = Assembly.LoadFrom(PluginPathFilename);
    var res = typeof(Contract).IsAssignableFrom(plugin.GetExportedTypes().Single());

    Assert.IsTrue(res); // Succeeds.
}

[TestMethod]
public void LoadingContractAndThenPlugin_Fails()
{
    var contract = Assembly.LoadFrom(ContractPathFilename);
    var plugin = Assembly.LoadFrom(PluginPathFilename);
    var res = typeof(Contract).IsAssignableFrom(plugin.GetExportedTypes().Single());

    Assert.IsTrue(res); // Fails.
}

使其难以测试:
如果我运行 LoadingContractAndThenPlugin_Fails 本身,则测试失败.但是,如果我一起运行测试,则取决于顺序.首先运行 SimplyLoadingPlugin_Succeeds ,最后运行 LoadingContractAndThenPlugin_Fails ,这两个测试都变为绿色,但以相反的顺序运行它们会使两个测试均失败.
所以不知何故,在Plugin之前,合同的大量加载给我带来了麻烦.
我无法在GAC中找到任何相关内容.

To make it harder to test:
If i run the LoadingContractAndThenPlugin_Fails test by itself is fails. But If i run the tests together it is dependent on order. Running SimplyLoadingPlugin_Succeeds first and LoadingContractAndThenPlugin_Fails last, makes both tests green but running them in the reverse order makes both fail.
So somehow the very loading of Contract before Plugin messes up something for me.
I can se nothing related in the GAC.

下面是所有需要的文件.可能必须更新其中的路径.
4个项目,每个文件一个文件.1个解决方案.

Below are all files needed. The paths in the probably have to be updated.
4 project with one file in each. 1 solution.

public abstract class Contract
{
    public abstract int Version { get; set; }
}

Plugin.cs(一个库项目)

public class Plugin : Contract
{
    public override int Version { get; set; }
}

Tests.cs(测试项目)

[TestClass]
public class Tests
{
    private const string PluginPath = @"C:\DATA\Projekt\LoadFromOrder\Plugin\bin\Debug";
    private string PluginPathFilename = Path.Combine(PluginPath, "Plugin.dll");
    private string ContractPathFilename = Path.Combine(PluginPath, "Contract.dll");

    [TestMethod]
    public void SimplyLoadingPlugin_Succeeds()
    {
        var plugin = Assembly.LoadFrom(PluginPathFilename);
        var res = typeof(Contract).IsAssignableFrom(plugin.GetExportedTypes().Single());

        Assert.IsTrue(res); // Succeeds.
    }

    [TestMethod]
    public void LoadingContractAndThenPlugin_Fails()
    {
        var contract = Assembly.LoadFrom(ContractPathFilename);
        var plugin = Assembly.LoadFrom(PluginPathFilename);
        var res = typeof(Contract).IsAssignableFrom(plugin.GetExportedTypes().Single());

        Assert.IsTrue(res); // Fails.
    }

    // BEGIN ---- Update. ----
    [TestMethod]
    public void LoadingPluginFromTestProject_Succeeds()
    {
        var contract = Assembly.LoadFrom(
            @"C:\DATA\Projekt\LoadFromOrder\TestProject\bin\Debug\Contract.dll");
        var plugin = Assembly.LoadFrom(PluginPathFilename);
        var res = typeof(Contract.Contract).IsAssignableFrom(plugin.GetExportedTypes().Single());

        Assert.IsTrue(res); // Succeeds.
    }
    // END ---- Update. ----

}

Program.cs(控制台项目)

class Program
{
    static void Main(string[] args)
    {
        var tests = new Tests();
        try
        {
            System.Console.WriteLine("Press A for Success and B for Fail.");
            switch (System.Console.ReadKey(true).Key)
            {
                case ConsoleKey.A:
                    tests.SimplyLoadingPlugin_Succeeds();
                    break;
                case ConsoleKey.B:
                    tests.LoadingContractAndThenPlugin_Fails();
                    break;
            }
            System.Console.WriteLine("SUCCESS");
        }
        catch (Exception exc)
        {
            System.Console.WriteLine($"FAIL: {exc.Message}");
        }
    }
}

推荐答案

通过使用 Assembly.LoadFrom 加载 Contract 来创建参考歧义.该库已经加载,这就是为什么我们可以执行 typeof(Contract)而不需要再次加载它的原因...

By loading the Contract using Assembly.LoadFrom you are creating a reference ambiguity. That library is already loaded that is why we can do typeof(Contract) no need to load it again...

我的建议::使用反射来确定您拥有哪些引用,并仅加载那些尚不存在的引用,这是示例代码片段:

My recommendation: use reflection to determine what references you have and only load those that are not already there, here is a sample code snippet:

        var dllFiles = Directory.GetFiles(DIR, "*.DLL", SearchOption.AllDirectories);
        var plugins = new HashSet<Assembly>();

        var references = typeof(Program).Assembly.GetReferencedAssemblies();
        foreach (var dllPath in dllFiles)
        {
            string name = Path.GetFileNameWithoutExtension(dllPath);
            if (!references.Any(x => x.Name == name) && !plugins.Any(x => x.GetName().Name == name))
                plugins.Add(Assembly.LoadFrom(dllPath));
        }

在该示例中,我们获得给定目录(包括子目录)的每个DLL,DIR可以是相对路径,例如 .. \ .. \ .. ,并且仅加载那些不在目录中的相对路径.程序集引用.

On that sample we get every DLL for a given directory (subdirectories included) the DIR can be a relative path like ..\..\.. and only load those that are not already in the assembly references.


这是带有两个插件项目的完整解决方案:
https://github.com/heldersepu/csharp-proj/tree/master/PluginSystem

这篇关于为什么我的程序集需要按特定顺序加载?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆