如何在使用反射加载的程序集中使用 Castle.Windsor

Posted

技术标签:

【中文标题】如何在使用反射加载的程序集中使用 Castle.Windsor【英文标题】:How to use Castle.Windsor in an assembly loaded using reflection 【发布时间】:2011-09-22 06:49:53 【问题描述】:

假设我有一个库 Lib.dll,它使用 Castle.Windsor 来初始化其服务。

我有一个主应用程序 App.exe,它使用反射在运行时加载 Lib.dll。 App.exe 事先并不知道 Lib.dll 的位置,只有在运行时才知道。

此时App.exe加载Lib.dll,Lib.dll初始化其服务时,会抛出System.TypeInitializationException异常,因为Castle.Windsor找不到服务类型。

Castle.MicroKernel.SubSystems.Conversion.ConverterException: Could not convert from 'Lib.TheServiceClass' to System.Type - Maybe type could not be found
   at Castle.MicroKernel.SubSystems.Conversion.TypeNameConverter.PerformConversion(String value, Type targetType) in e:\OSS.Code\Castle.Windsor\src\Castle.Windsor\MicroKernel\SubSystems\Conversion\TypeNameConverter.cs:line 91
   at Castle.MicroKernel.SubSystems.Conversion.DefaultConversionManager.PerformConversion(String value, Type targetType) in e:\OSS.Code\Castle.Windsor\src\Castle.Windsor\MicroKernel\SubSystems\Conversion\DefaultConversionManager.cs:line 134
   at Castle.MicroKernel.SubSystems.Conversion.DefaultConversionManager.PerformConversion[TTarget](String value) in e:\OSS.Code\Castle.Windsor\src\Castle.Windsor\MicroKernel\SubSystems\Conversion\DefaultConversionManager.cs:line 162
   at Castle.Windsor.Installer.DefaultComponentInstaller.SetUpComponents(IConfiguration[] configurations, IWindsorContainer container, IConversionManager converter) in e:\OSS.Code\Castle.Windsor\src\Castle.Windsor\Windsor\Installer\DefaultComponentInstaller.cs:line 196
   at Castle.Windsor.Installer.DefaultComponentInstaller.SetUp(IWindsorContainer container, IConfigurationStore store) in e:\OSS.Code\Castle.Windsor\src\Castle.Windsor\Windsor\Installer\DefaultComponentInstaller.cs:line 52
   at Castle.Windsor.WindsorContainer.Install(IWindsorInstaller[] installers, DefaultComponentInstaller scope) in e:\OSS.Code\Castle.Windsor\src\Castle.Windsor\Windsor\WindsorContainer.cs:line 327
   at Castle.Windsor.WindsorContainer.Install(IWindsorInstaller[] installers) in e:\OSS.Code\Castle.Windsor\src\Castle.Windsor\Windsor\WindsorContainer.cs:line 674

显然 Castle 找不到我的服务类,因为它位于不在 App.exe 目录中的 Lib.dll 中。当我将 Lib.dll 复制到 App.exe 目录时,问题就消失了,但必须复制它不是我们想要的。

那么我在 Lib.dll 中的代码如何告诉 Castle.Windsor 将类加载到正确的位置? (在 Lib.dll 位置而不是在 App.exe 位置)

【问题讨论】:

您使用的是哪个版本。容器中的组件是如何注册的? @KrzysztofKoźmic 2.5.2 组件的注册方式是什么意思?我使用 XML 文件来定义服务 你能分享 XML 文件并展示你是如何加载它的吗? @KrzysztofKoźmic new WindsorContainer().Install(Configuration.FromXmlFile(configFile)); 也可以参考上面的堆栈跟踪 好的,我们正在慢慢实现。这段代码在哪里调用? 【参考方案1】:

您可以尝试通过 AssemblyResolve 事件在代码中加载未解析的程序集

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>

     string typeToLoad = args.Name;
     string myPath = new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName;
     return Assembly.LoadFile(...); //or return Assembly.GetExecutingAssembly() etc.
;

【讨论】:

【参考方案2】:

您可能会考虑将插件加载到单独的 AppDomain 中,使用不同的私有路径,请查看 AppDomainSetup。当然,您的插件需要一个单独的应用程序域是一个缺点,但有时这被认为是一种很好的做法。

【讨论】:

容易吗?我可以将插件的路径添加到主应用程序的应用程序域吗? AFAIK addtorpivatepath 被声明为过时:msdn.microsoft.com/en-us/library/…,并且它仅在插件位于子目录中时才有效... 有没有比在单独的 AppDomain 中加载更简单的方法?主要应用程序不在我的控制之下,我非常怀疑负责人是否愿意为解决我的问题而付出这些努力【参考方案3】:

如果 App.exe 没有为您提供 Castle Windsor 的容器实例来配置您的服务,您可能无法以简单而优雅的方式进行操作。

如果不直接暴露,或许可以使用Service Locator访问?还是使用 App.exe 程序集上的反射自行查找?

最好的解决方案是 App.exe 中的代码调用库中的特定方法(即,它可以查找特定的接口实现,例如 IModuleInitializer 或其他东西,创建它的实例并调用某种 Initialize方法将容器实例传递给您的代码)。

您也可以考虑像 MEF 这样的可扩展性框架,但这可能有点矫枉过正并对 App.exe 产生重大影响。

【讨论】:

以上是关于如何在使用反射加载的程序集中使用 Castle.Windsor的主要内容,如果未能解决你的问题,请参考以下文章

学习--反射

Reflection(反射)

通过反射从加载的程序集中返回 Types[] 时的 FileNotFound

C#怎么使用反射获取事件的响应方法

温故知新之:反射:加载程序集中的类,动态调用

反射资料整理