我正在编写一个简单的插件,偶然发现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解决方案。
public class Plugin : Contract
{
public override int Version { get; set; }
}
[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. ----
}
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}");
}
}
}
{{1}}
答案 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
定义。无论您的架构有什么用例,都不是装配加载是否应该让您担心;这是插件是否已正确实现。