2013-02-25 79 views
5

我正在考慮使用MEF來解決插件管理需求。在blurb中,它表示「沒有硬性依賴」,但據我所知,導入/導出接口很難依賴。MEF和版本

我擔心的是這個。我的可擴展應用程序是由我寫的。插件由第三方編寫。因此,讓我們說我們都從V1開始。我的應用程序定義了插件「部分」需要實現的接口IPlugin。我們部署應用程序,用戶安裝一堆第三方插件。一切都很好。

現在我升級我的應用程序,我想添加一個新的方法到插件接口。我看到它的方式我有兩個選擇:

  1. 編輯接口 - 可能是壞的,這會破壞現有的插件,因爲它們不再正確地實現接口。
  2. 創建一個新的 'V2' 接口,即從原來的

    公共接口IPluginV2繼承:IPlugin {}

現在我有一個問題。我的用戶都擁有大量實施IPlugin的第三方插件,但我現在要求他們實施IPluginV2。我認爲這些第三方插件將不再起作用,直到開發人員實現新的界面。

MEF有辦法處理這種情況嗎?我非常希望能夠讓我的應用發展,同時讓老插件繼續工作,而不必重建。最好的處理方式是什麼?

回答

1

只是一對夫婦的建議(我沒有測試),可以幫助頭腦風暴可以爲你解決:

  1. 如果使用MEF,請爲每個版本的不同AggregateCatalog。這樣你就可以同時維護V1和V2插件

  2. 如果不使用MEF,來自第三方的動態加載的DLL應該返回它已經實現的當前接口版本,並且你可以選擇根據版本號

這有幫助嗎?

乾杯,dimamura

11

對於版本,您可能需要爲每個版本的接口adapter pattern它們之間去。這就是System.AddIn如何處理版本控制,它也適用於MEF。

比方說,我們有以下幾種類型的應用程序的V1

public interface IPlugin 
{ 
    string Name { get; } 
    string Publisher { get; } 
    string Version { get; } 

    void Init(); 
} 

這是我們V1插件感知應用程序的唯一合同。它包含在程序集Contracts.v1中。

然後我們有一個V1插件:

[Export(typeof(IPlugin))] 
public class SomePlugin : IPlugin 
{ 
    public string Name { get { return "Some Plugin"; } } 

    public string Publisher { get { return "Publisher A"; } } 

    public string Version { get { return "1.0.0.0"; } } 

    public void Init() { } 

    public override string ToString() 
    { 
     return string.Format("{0} v.{1} from {2}", Name, Version, Publisher); 
    } 
} 

這是導出爲IPlugin。它包含在程序集Plugin.v1中,併發布在主機的應用程序基路徑下的「插件」文件夾中。

最後V1主持人:

class Host : IDisposable 
{ 
    CompositionContainer _container; 

    [ImportMany(typeof(IPlugin))] 
    public IEnumerable<IPlugin> Plugins { get; private set; } 

    public Host() 
    { 
     var catalog = new DirectoryCatalog("plugins"); 
     _container = new CompositionContainer(catalog); 
     _container.ComposeParts(this); 
    } 

    public void Dispose() { _container.Dispose(); } 
} 

其中進口文件夾 「插件」 中找到的所有IPlugin部分。

然後,我們決定出版V2因爲我們希望提供的版本,我們將需要versionless合同:

public interface IPluginV2 
{ 
    string Name { get; } 
    string Publisher { get; } 
    string Version { get; } 
    string Description { get; } 

    void Init(IHost host); 
} 

一個新的屬性和方法的方法簽名。此外,我們增加一個接口主機:

public interface IHost 
{ 
    //Here we can add something useful for a plugin. 
} 

上述這些問題都包含在裝配Contracts.v2

要允許版本我們添加一個插件適配器從V1到V2:

class V1toV2PluginAdapter : IPluginV2 
{ 
    IPlugin _plugin; 

    public string Name { get { return _plugin.Name; } } 

    public string Publisher { get { return _plugin.Publisher; } } 

    public string Version { get { return _plugin.Version; } } 

    public string Description { get { return "No description"; } } 

    public V1toV2PluginAdapter(IPlugin plugin) 
    { 
     if (plugin == null) throw new ArgumentNullException("plugin"); 
     _plugin = plugin; 
    } 

    public void Init(IHost host) { plugin.Init(); } 

    public override string ToString() { return _plugin.ToString(); } 
} 

這只是適應從IPluginIPluginV2。它返回一個固定的描述,並在Init中對主機參數不做任何處理,但它從合同中調用無參數Init

最後的V2主持人:

class HostV2WithVersioning : IHost, IDisposable 
{ 
    CompositionContainer _container; 

    [ImportMany(typeof(IPluginV2))] 
    IEnumerable<IPluginV2> _pluginsV2; 

    [ImportMany(typeof(IPlugin))] 
    IEnumerable<IPlugin> _pluginsV1; 

    public IEnumerable<IPluginV2> Plugins 
    { 
     get 
     { 
      return _pluginsV1.Select(p1 => new V1toV2PluginAdapter(p1)).Concat(_pluginsV2); 
     } 
    } 

    public HostV2WithVersioning() 
    { 
     var catalog = new DirectoryCatalog("plugins"); 
     _container = new CompositionContainer(catalog); 
     _container.ComposeParts(this); 
    } 

    public void Dispose() { _container.Dispose(); } 
} 

其中進口都IPluginIPluginV2部分,以適應每個IPluginIPluginV2,並公開所有發現的插件連接序列。適應完成後,所有插件都可以視爲V2插件。

您也可以使用主機的接口適配器模式允許V2插件與V1主機配合使用。

另一種方法是autofac IoC,它可以與MEF配合使用integrate,並且可以使用adapters支持版本控制。