如何在不使用AppDomains的情况下实现.net插件?

问题陈述:实现一个插件系统,允许覆盖关联的程序集(避免文件锁定)。 在.Net中,可能无法卸载特定程序集,只能卸载整个AppDomain。

我发布这个是因为当我试图解决问题时,每个解决方案都参考了使用多个AppDomain。 多个AppDomain很难正确实现,即使在项目开始时进行架构也是如此。

另外,AppDomains对我不起作用,因为我需要跨域传输Type作为Speech Server worfklow的InvokeWorkflow活动的设置。 不幸的是,跨域发送类型会导致程序集注入本地AppDomain。

此外,这与IIS相关。 IIS具有卷影复制设置,允许执行程序集在加载到内存时被覆盖。 问题是(至少在XP下,没有在生产2003服务器上测试)当您以编程方式加载程序集时,卷影副本不起作用(因为您正在加载DLL,而不是IIS)。

  1. 检查组件是否已加载(以避免因反复加载同一组件而导致的内存泄漏)。
  2. 如果未加载,请将程序集读入字节数组。 这样可以防止锁定文件。
  3. 将字节数组作为Assembly.Load的争论提供

以下代码假定您知道程序集的FullName。

Assembly assembly = null; foreach(Assembly loadedAssembly in AppDomain.CurrentDomain.GetAssemblies()) if (loadedAssembly.FullName == "foobar, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null") assembly = loadedAssembly; if(assembly == null) { byte[] studybin = System.IO.File.ReadAllBytes(@"C:\pathToAssembly\foobar.dll"); assembly = Assembly.Load(studybin); } 

请注意,如果您尝试在目录中查找特定程序集,则可以运行“System.Reflection.AssemblyName.GetAssemblyName(path);” 查看FullName是否与您要查找的内容相匹配。 ‘GetAssemblyName(path)’不会将程序集注入当前的AppDomain。

另请注意,此解决方案不适用于必须很少重新启动并且程序集以高频率更改的应用程序。 每次加载程序集时,应用程序的内存占用量都会增加。 没有方法可以卸载程序集,因此缩小内存使用量的唯一选择是重新启动应用程序。 但是,此限制通常优于使用多个应用程序域的大型性能,内存和代码复杂性开销。 如果您希望使用Type s,这种限制也是不可避免的。

如果你想隔离你的插件,你必须……

  1. 创建一个扩展MarshallByRefObject的类型,它将与你的插件交互(让我们称之为FOOMASTER)
  2. 创建一个新的appdomain(让我们称之为addinLand)
  3. 调用addinLand.CreateInstanceAndUnwrap在addinLand中创建FOOMASTER实例并在当前应用程序域中获取代理
  4. 告诉FOOMASTER加载你的插件

现在您的插件已加载到addinLand中,您可以通过FOOMASTER代理从主应用程序域与它们进行交互。 当您的插件崩溃时,他们不会删除您的应用程序。

这是一个有趣且有点混乱的过程。 我最初认为这个想法是加载你的插件,然后将它们作为透明代理带到当前的应用程序域中,但最好的设计是将插件保留为与你创建的更复杂类型(FOOMASTER)交互的简单对象, MarshallByRefObject,加载在addinLand应用程序域中,与您交互的透明代理。

CLR Via C#的第21章和第22章对理解该过程非常有帮助。

如果您对构建具有插件体系结构的系统的严肃方法感兴趣,您可能需要查看MAF(托管外接程序框架),它现在是.NET Framework的一部分,即System.AddIn命名空间。

它将帮助您管理加载项隔离和版本控制,甚至可以向后兼容合同。

这里有一点学习曲线,所以这可能不是你想要的。

http://msdn.microsoft.com/en-us/library/bb384200.aspx