为什么我的装配需要按特定顺序加载?

时间:2017-08-09 10:28:38

标签: c# .net

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

在插件上调用IsAssignableFrom会按预期返回True 如果我在加载插件之前加载契约程序集,则插件上的[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. } 返回False。

我正在运行Win10和dotnet4.7,但我怀疑它有任何相关性。

代码

LoadingContractAndThenPlugin_Fails

使测试更难:
如果我自己运行SimplyLoadingPlugin_Succeeds测试失败了。但如果我一起运行测试,它依赖于订单。首先运行LoadingContractAndThenPlugin_Fails,最后运行public abstract class Contract { public abstract int Version { get; set; } } ,使两个测试都变为绿色,但以相反的顺序运行它们都会失败。
因此不知何故,插件之前的合同装载会给我带来麻烦 我无法在GAC中找到相关内容。

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

Contract.cs(图书馆项目)

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

Plugin.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. ----

}

Tests.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}");
        }
    }
}

Program.cs(控制台项目)

{{1}}

2 个答案:

答案 0 :(得分:10)

使用Contract加载Assembly.LoadFrom,您正在创建参考歧义。该库已经加载,这就是为什么我们可以typeof(Contract)无需再次加载它...

我的建议:使用反射来确定您拥有的参考资料,并仅加载那些尚未存在的参考资料,以下是示例代码段:

        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可以是..\..\..之类的相对路径,并且只加载那些尚未在程序集引用中的路径。


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

答案 1 :(得分:1)

据@HelderSepu声称,第二次装载合同装配可能是问题。

我建议您以不同但更简单的方式进行测试。不要在测试中手动(重新)加载程序集,只需添加对测试项目中的程序集/项目的引用,并直接引用typeof(Contract)typeof(Plugin)并检查是否typeof(Contract).IsAssignableFrom(typeof(Plugin))。没有什么复杂的,只需添加对测试项目的引用即可。

您无需测试程序集是否正确加载,CLR将处理该程序集。您需要测试插件程序集是否包含Contract定义。无论您的架构有什么用例,都不是装配加载是否应该让您担心;这是插件是否已正确实现。