MEF运行时插件更新问题

10

问题

我的MEF代码在运行时无法正确更新与DirectoryCatalog相关联的文件夹中的程序集。插件在运行时成功加载,但当我更新dll并在DirectoryCatalog上调用Refresh时,程序集没有得到更新。

背景

我正在构建一个包含MEF容器的dll,并使用DirectoryCatalog查找本地插件文件夹。我目前从一个简单的WinForm中调用这个dll,该WinForm已设置为使用ShadowCopy的单独项目,以便我可以覆盖插件文件夹中的dll。我不是使用FileWatcher来更新此文件夹,而是公开了一个公共方法,在其中调用DirectoryCatalog上的refresh,因此我可以随意更新程序集,而不是自动更新。

代码

基类实例化MEF目录和容器,并将它们保存为类变量以供稍后引用。

    public class FieldProcessor
{
    private CompositionContainer _container;
    private DirectoryCatalog dirCatalog;

    public FieldProcessor()
    {
        var catalog = new AggregateCatalog();
        //Adds all the parts found in the same assembly as the TestPlugin class
        catalog.Catalogs.Add(new AssemblyCatalog(typeof(TestPlugin).Assembly));
        dirCatalog = new DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory + "Plugin\\");
        catalog.Catalogs.Add(dirCatalog);

        //Create the CompositionContainer with the parts in the catalog
        _container = new CompositionContainer(catalog);
    }

    public void refreshCatalog()
    {
        dirCatalog.Refresh();
    }

} ...

这是我想要覆盖的插件。我的测试方式是,将返回的字符串输出到文本框中,然后更改插件返回的字符串,重新构建,并将其复制到插件文件夹中。但在运行的应用程序中,它不会更新,直到我关闭并重新启动应用程序。

[Export(typeof(IPlugin))]
[ExportMetadata("PluginName", "TestPlugin2")]
public class TestPlugin2 : IPlugin
{
    public IEnumerable<IField> GetFields(ContextObject contextObject, params string[] parameters)
    {
        List<IField> retList = new List<IField>();
        //Do Work Return Wrok Results
        retList.Add(new Field("plugin.TestPlugin2", "TestPluginReturnValue2"));
        return retList;
    }
}

编辑

导入语句

    [ImportMany(AllowRecomposition=true)]
    IEnumerable<Lazy<IPlugin, IPluginData>> plugins;

研究

我进行了相当广泛的研究,无论在文章和代码示例中都表明,答案是向容器添加DirectoryCatalog并保存该目录的引用,然后在添加新插件后调用该引用上的Refresh方法,它将更新程序集...我正在做这个,但它没有显示来自新插件DLL的更新输出。

请求

有人遇到过这个问题吗?或者知道导致我的运行时程序集未更新的原因吗?任何额外的信息或洞察力都会受到赞赏。

解决方案

感谢Panos和Stumpy提供的链接,他们带领我找到了解决我的问题的方法。对于未来的知识探寻者,我的主要问题是,当新程序集与被覆盖的DLL具有完全相同的程序集名称时,Refresh方法不会更新程序集。对于我的POC,我只是测试了使用日期附加到程序集名称和其他所有内容相同的方式进行重建,它工作得非常好。他们在下面的评论中提供的链接非常有用,如果您遇到相同的问题,建议使用这些链接。


1
DirectoryCatalog.Refresh 方法无法检测到已更新的程序集,只能检测到新增或删除的程序集。请参考此答案中的解决方法和建议:https://dev59.com/qm7Xa4cB1Zd3GeqPoVCI#14842417 - Panos Rontogiannis
我的dll在加载时被锁定,因此我无法用新的dll覆盖它们。你没有遇到这个问题吗?你做了什么让它们可以更新的吗? - Poul K. Sørensen
是的,我确实遇到了这个问题。我提到的其中一个步骤是启用“影子复制”。影子复制允许程序拉取dll程序集的本地副本,并将它们添加到本地缓存中,而不是锁定dll文件。必须启用此功能才能在运行时“热交换”dll文件,否则您需要停止程序,更改dll文件,然后重新启动它。我认为这是我查看的示例,但如果对您不起作用,请搜索MEF和Shadow Copy,https://dev59.com/6mcs5IYBdhLWcg3ww2tP - Mabdullah
1个回答

3
你是否将AllowRecomposition参数设置到了你的导入属性中?
AllowRecomposition
Gets or sets a value that indicates whether the property or field will be recomposed when exports with a matching contract have changed in the container.

http://msdn.microsoft.com/en-us/library/system.componentmodel.composition.importattribute(v=vs.95).aspx

编辑:

DirectoryCatalog不会更新程序集,只会添加或删除:http://msdn.microsoft.com/en-us/library/system.componentmodel.composition.hosting.directorycatalog.refresh.aspx

解决方法如下:https://dev59.com/qm7Xa4cB1Zd3GeqPoVCI#14842417


我已经更新了问题以反映我的导入语句。抱歉我之前没有发布导入语句的代码。 - Mabdullah
你的刷新代码是什么?我认为问题在里面。你说不使用FileSystemWatcher,那你如何检测和重新加载你的程序集? - Niels
我通过上面看到的RefreshCatalog方法来公开它。我从winForm上的按钮单击事件中调用该方法。 - Mabdullah
1
刷新操作不会更新程序集,只会添加或删除它们。请参阅http://msdn.microsoft.com/en-us/library/system.componentmodel.composition.hosting.directorycatalog.refresh.aspx和https://dev59.com/qm7Xa4cB1Zd3GeqPoVCI#14842417。 - Niels
1
谢谢提供的链接,我正在进一步调查,可能需要稍微改变用例,但这些链接似乎会极大地帮助我。一旦我解决了问题,我会回复并将其标记为答案。再次感谢。 - Mabdullah
1
这是一个汇编命名的问题,一旦我让代码使用唯一的汇编名称,代码就像冠军一样工作了。感谢您的所有帮助。 - Mabdullah

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接