我有一个服务,它充当内容规范化的脚本引擎。该服务通过在启动时动态加载DLL来扩展其脚本语言,将上下文对象接口传递给每个DLL中的Load()方法。上下文对象为DLL提供了将字符串名称绑定到工厂对象,命令/仿函数,脚本对象包装器等的方法。
我遇到一个问题,当调用其中一个DLL的Load()方法时,如果它尝试调用其中一个绑定方法,则会抛出MissingMethodException。该方法采用在另一个DLL中声明的接口,并且我怀疑在通过Type.GetMethod.Invoke()传递上下文对象时,方法签名正在发生。
应该注意,仅当动态通过Assembly.LoadFile()加载DLL时才会引发异常。在主进程中引用DLL并使用反射来获取类/方法,然后从那里动态调用它会导致抛出异常。
我的代码/项目参考设置......
Content.dll: 什么都没引用
public class ContentPackage { ... }
public interface IContentDataSource
{
ContentPackage Receive();
void Send(ContentPackage content);
}
public interface IContentDataSourceFactory
{
IContentDataSource CreateInstance(string param);
IEnumerable<IContentDataSource> CreateInstances(string param);
}
public class ContentXmlDataSource : IContentDataSource
{
ContentPackage IContentDataSource.Receive() { ... }
void IContentDataSource.Send(ContentPackage content) { ... }
public class Factory : IContentDataSourceFactory
{
IContentDataSource IContentDataSourceFactory.CreateInstance(string param) { return new ContentXmlDataSource(param); }
IEnumerable<IContentDataSource> IContentDataSourceFactory.CreateInstances(string param) { ... }
IContentDataSourceFactory.CreateInstance(
public static Factory Singleton { get { return s_Singleton; } }
private static Factory m_Singleton = new Factory();
}
}
ExtensionInterface.dll: 引用Content.dll
public interface IXmlTagCommand() { ... }
public interface IExtensionBindingContext
{
void BindCommand(string name, IXmlTagCommand cmd);
void BindDataSourceFactory(string name, IContentDataSourceFactory factory);
}
Service.dll: 引用ExtensionInterface.dll,Content.dll
public partial class TheService
{
public class ExtensionBindingContext : IExtensionBindingContext
{
void IExtensionBindingContext.BindCommand(string name, IXmlTagCommand cmd)
{ ... }
void BindDataSourceFactory(string name, IContentDataSourceFactory factory)
{ ... }
ExtensionBindingContext(string basePath)
{
var paths = Directory.GetFiles(basePath, "*.dll");
foreach (var path in paths)
{
Assembly assembly = Assembly.LoadFile(path);
Type t = assembly.GetType("ExtensionLibrary");
MethodInfo method = t.GetMethod("Load",
BindingFlags.Public | BindingFlags.Static);
method.Invoke(null, new object[] { this }); // Throws exception!
}
}
}
TheService()
{
(new ExtensionBindingContext()).LoadExtensions(".\DLLFolder\");
}
}
Extension.StandardContentFactories.dll:(动态加载) 引用ExtensionInterface.dll,Content.dll
public class ExtensionLibrary
{
public void Load(IExtensionBindingContext context)
{
context.BindCommand("SomeCommand", XTagCommands.SomeCommand); // Fine: No exception.
context.BindDataSourceFactory("ContentXml", ContentXmlFactory.Singleton); // Causes whole function to throw exception if not commented out, preventing even the "BindCommand" call to not be reached.
}
}
特殊情况:
异常: System.Reflection.TargetInvocationException,{“调用目标已抛出异常。”}
内部例外: {“找不到方法:'Void SomeNamespace.ContentService.IExtensionBindingContext.BindDataSourceFactory(System.String,SomeNamespace.Content.IContentDataSourceFactory)'。”}
如果我在StandardContentFactories.dll中注释掉BindDataSourceFactory()行,问题就会消失。我也以这种方式成功加载了多个其他DLL。
我尝试过以下方法: - 从项目检查GAC文件夹中相同的命名程序集,找不到任何文件夹 - 清洁解决方案和重建 - 验证所有程序集都使用相同的.NET(4.0,而不是客户端配置文件) - 将接口传递给包装器对象,改变方法以获取包装器:而不是找到字段 - 将工厂对象作为“对象”传递(也更新方法参数),然后将该对象强制转换回工厂对象继承的接口:无效的强制转换
通过动态程序集加载和调用接口后面的对象是否存在某种问题,如果该接口有一个接受另一个引用程序集中定义的接口的方法?如果接口问题不可解决,是否有可能解决工厂与未知DLL的动态绑定?
修改
按照mike z的建议检查模块后,我看到它们是每个加载的共享接口DLL的两个副本(Content.dll和ExtensionInterface.dll);第一组DLL是从服务的主机进程bin \ Debug目录加载的;第二组DLL正在从Assembly.LoadFile()加载的第一个插件DLL的bin \ Debug目录中加载。
该进程使用一组目录路径来搜索插件中的DLL,并且我能够强制进程找到/加载首先出现问题的DLL,这导致问题消失。我猜测所有Assembly.LoadFile()加载的DLL都使用一组共享接口DLL,这些DLL作为第一次调用Assembly.LoadFile()(即第一个插件DLL)的副作用而加载。加载这些DLL时,它们可能只包含IExtensionBindingContext.BindCommand()的接口信息,但不包含IExtensionBindingContext.BindDataSourceFactory()和/或IContentDataSourceFactory,因为它们不使用它们。
有人知道这个评估是否正确吗?如果是这样,是否有(标准或正确的)方法强制我的Assembly.LoadFile()DLL正确加载共享库中的所有接口/方法信息? (我宁愿不使用人为的解决方案,例如从配置文件强制加载顺序或创建一个在开始时使用所有功能的假库。)