MEF 和 ShadowCopying DLL,以便我可以在运行时覆盖它们

Posted

技术标签:

【中文标题】MEF 和 ShadowCopying DLL,以便我可以在运行时覆盖它们【英文标题】:MEF and ShadowCopying DLLs so that I can overwrite them at runtime 【发布时间】:2012-09-26 01:22:19 【问题描述】:

我试图在我的 MEF 插件目录中停止我的应用程序锁定 DLL,以便我可以在运行时覆盖程序集(请注意,我实际上并没有试图让 MEF 即时重新加载它们,在下一个应用程序启动时很好,我只是不想停止应用程序进行复制)

我试图通过为我的 mef 加载的程序集创建一个影子复制的应用程序域来做到这一点,如下所示:

[Serializable]
    public class Composer:IComposer
    
        private readonly string _pluginPath;
        public Composer(IConfigurePluginDirectory pluginDirectoryConfig)
        
            _pluginPath = pluginDirectoryConfig.Path;
            var setup = new AppDomainSetup();
            setup.ShadowCopyFiles = "true"; // really??? is bool not good enough for you?
            var appDomain = AppDomain.CreateDomain(AppDomain.CurrentDomain.FriendlyName + "_PluginDomain", AppDomain.CurrentDomain.Evidence, setup);

            appDomain.DoCallBack(new CrossAppDomainDelegate(DoWorkInShadowCopiedDomain));      
        

        private void DoWorkInShadowCopiedDomain()
        
            // This work will happen in the shadow copied AppDomain.

            var catalog = new AggregateCatalog();
            var dc = new DirectoryCatalog(_pluginPath);
            catalog.Catalogs.Add(dc);
            Container = new CompositionContainer(catalog);
        

        public CompositionContainer Container  get; private set; 
    

然后通过此类上的 CompositionContainer 访问我的 MEF 组件目录。然而,组合容器似乎只在影子复制域内初始化(这是有道理的),这意味着它在我的应用程序域中为空。我只是想知道是否有更好的方法来执行此操作或跨域查询以获取我的 MEF 组件

【问题讨论】:

您似乎可以使用自己手动创建的影子目录对插件目录中的所有文件调用 File.Copy。然后,您只需从主域中的影子路径加载。 我想过这个问题,但我的理解是这是影子复制应该解决的问题(因此他们可能有更多的边缘情况与此有关)。你知道shadowcopying是否比这更大或者手动复制是否足够好? 【参考方案1】:

如果您不想遵循 Dan Bryant 和 zync 的解决方案,您可以创建一个 shell 应用程序,在新的 AppDomain 中简单地执行您的应用程序。

一种方法是:

    创建一个新的应用程序项目,它将成为 shell 应用程序。 在shell 应用程序中,创建AppDomain,启用卷影复制,如果需要,指定启用卷影复制的目录。 使用AppDomain.ExecuteAssembly 调用您当前的应用程序。

如果你有一个类库而不是应用程序,你可以尝试以下方法:

    创建一个新的类库项目。

    在新建的类库项目中添加如下接口:

    public interface IRemoteLoader  
      
        void Load();  
        void Unload();  
    
    

    将此接口的实现添加到需要在新 AppDomain 中执行的类库中。在LoadUnload 方法中,您应该添加代码以分别执行初始化和清理。使类派生自MarshalByRefObject。这是 .NET Remoting 在两个 AppDomain 上创建代理对象所必需的。

    创建新的 AppDomain 后,使用CreateInstanceAndUnwrap 从步骤 3 创建加载程序类的实例。

    在第 4 步创建的对象上使用 LoadUnload

如果您不进行细粒度控制并且只需启动/停止就足够了。

【讨论】:

@panosrontogainnis 是的,我同意这似乎是解决问题的最受支持的方式(即引导/影子复制整个应用程序。不幸的是,在我的情况下,我不控制引导程序(因为我在NServiceBus.Hosted)所以这个解决方案不会特别适合我。话虽如此,我觉得它可能是其他人最好的解决方案。 干杯。我添加了另一种适用于类库的方法。【参考方案2】:

这种场景更接近于移动应用中的自动更新功能。本质上,如果在 App Start/Restart 上可用,您希望获取新的程序集。

一种设计方法可能是使用一种通信机制在启动时向您的应用发出信号,表明新程序集可用(可能是 version.txt 文件)。如果是,那么相同的 version.txt 文件可以指向程序集的新位置。是的 - 您最终可能会创建许多子文件夹来指向正确的版本,但这些可以由另一个进程清理。

你可以使用这样的层次结构 -

版本\ - 版本1.0\ - 版本2.0\

这种类型的设计更接近于众所周知的自动更新范例。

【讨论】:

将此添加为新答案,因为它是解决整体问题而不是具体问题的不同方法。 我认为您建议的方法与 Dan Bryants 之前的建议相同,即手动执行与卷影复制相同的功能。我觉得这样做有点笨拙,因为它基本上是在克隆现有的 .NET 函数。我只是想在执行此操作之前确保没有绕过跨域访问的好方法。【参考方案3】:

您是否可以选择不使用 DirectoryCatalog 并使用 AssemblyCatalog 加载目录中的所有程序集?您甚至可以转到代码 plex 并从 DirectoryCatalog 类中复制相同的代码,该类读取目录并加载程序集。

您将失去动态加载它们的能力,但正如您所提到的,这并不是真正的要求。

【讨论】:

嘿@zync 也许我错过了你的意思但是我如何通过实现我自己的 DirecoryCatalog 来解决卷影复制/跨应用程序域问题。有没有办法告诉它从单独的应用程序域中提取?也许您可以添加更多详细信息来说明您的意思 @LukeMcGregor - 我的建议行不通。但我做了一些挖掘,这并不是一个 MEF 问题。 appdomain 正在锁定任何接触的程序集。 ***.com/questions/2745093/overwriting-dlls-in-mef 是的,你是对的,我的问题不是专门的 MEF 问题,它更多的是卷影副本/应用程序域问题。

以上是关于MEF 和 ShadowCopying DLL,以便我可以在运行时覆盖它们的主要内容,如果未能解决你的问题,请参考以下文章

使用 MEF 构建具有 n 层松散耦合的 MVC ASP.NET 应用程序

使用 app.config 配置卷影复制

MEF 的 DirectoryCatalog 是如何工作的?

C#可扩展编程之MEF学习笔记:MEF的导出(Export)和导入(Import)

什么是 MEF?

.Net插件编程模型:MEF和MAF