2011-06-21 13 views
14

有人能解釋一下SatisfyImportsOnceComposeParts之間的區別,以及爲什麼一個人可以在另一個人不工作的地方工作?SatisfyImportsOnce vs ComposeParts

具體來說,我有一個MVC Web應用程序,我正在使用MEF。以下是一些代碼(來自該應用程序),當我使用SatisfyImportsOnce時工作,但不使用ComposeParts時。我的理解是,ComposeParts從屬性對象數組中創建可組合部分,並將它們組合到指定的組合容器中,並且通過使用指定的組合服務來組合指定的部分。對我簡單的猴子大腦來說,即使英語不同,它們在語義上也是一樣的。兩者都使用CompositionContainer在導入目標處吐出導出的類型。

public class FormPartCustomatorFactory 
{ 

    [ImportMany(typeof(ICustomRenderer), AllowRecomposition = true)] 
    private readonly List<Lazy<ICustomRenderer, ICustomRendererMetaData>> _rendererImports = new List<Lazy<ICustomRenderer, ICustomRendererMetaData>>(); 

    private readonly Dictionary<string, Lazy<ICustomRenderer, ICustomRendererMetaData>> _renderers; 

    public static readonly FormPartCustomatorFactory Instance = new FormPartCustomatorFactory(); 

    static CompositionContainer _container; 

    private FormPartCustomatorFactory() 
    { 
     using (var catalog = new DirectoryCatalog(HttpRuntime.BinDirectory, "*.dll")) 
     {    
      _container = new CompositionContainer(catalog); 
      _container.SatisfyImportsOnce(this); // <- Works 
      // _container.ComposeParts(this); // DOESN'T Work 
      _renderers = _rendererImports.ToDictionary(q => q.Metadata.Name, q => q); 
     } 
    } 

    ~FormPartCustomatorFactory() 
    { 
     _container.Dispose(); 
    } 

    public static ICustomRenderer Find(string name) 
    { 
     return Instance._renderers[name].Value; 
    } 
} 

回答

20

SatisyImportsOnce將構成一個沒有註冊它的類型進行重新組合。因此,如果您打算使用不支持重構的類型,則可以使用SatisfyImportsOnce,它將像往常一樣完成工作,但是容器中的任何更改(添加的新零件或部件都會被刪除),則實例將不會自動重新提供這些新的部件。

在您的實例,使用的是:

[ImportMany(typeof(ICustomRenderer), AllowRecomposition = true)] 

...但是通過SatisfyImportsOnce,這種進口將不會被重新構圖一樣。

如果您不擔心重構,你可以改變你的代碼中使用構造器注入,所以你可以這樣做:

[ImportingConstructor] 
public FormPartCustomatorFactory(IEnumerable<Lazy<ICustomRenderer, ICustomRendererMetadata>> renderers) 
{ 
    if (renderers == null) 
     throw new ArgumentNullException("renderers"); 

    _renderers = renderers.ToDictionary(r => r.Metadata.Name, r => r); 
} 

我之所以建議構造函數注入,是一組Lazy<ICustomRenderer, ICustomRendererMetadata>實例是一個顯式依賴你的類型需要,所以它最好是實例化你的類型在一個可用的狀態,而不是實例化,然後需要一個額外的步驟準備第一次使用。

這使得您的FormPartCustomatorFactory類型更具可測性。爲此,如果您要更改構造函數,那麼將其設置爲單例的方法將不起作用。相反,你可以利用MEF的生命週期管理功能,所以有可能重新設計類型:

public interface IFormPartCustomatorFactory 
{ 
    ICustomRenderer Find(string name); 
} 

[Export(typeof(IFormPartCustomerFactory)), PartCreationPolicy(CreationPolicy.Shared)] 
public class FormPartCustomatorFactory : IFormPartCustomatorFactory 
{ 
    private IEnumerable<Lazy<ICustomRenderer, ICustomRendereMetadata>> _renderers; 

    [ImportingConstructor] 
    public FormPartCustomatorFactory(IEnumerable<Lazy<ICustomRenderer, ICustomRendererMetadata>> renderers) 
    { 
     if (renderers == null) 
      throw new ArgumentNullException("renderers"); 

     _renderers = renderers; 
    } 

    public ICustomRenderer Find(string name) 
    { 
     return _renderers 
      .Where(r => r.Metadata.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase) 
      .Select(r => r.Value) 
      .FirstOrDefault(); 
    } 
} 

這樣做,這樣意味着你的類型是不依賴於MEF,它可以沒有它被使用,其更易測試,並且CompositionContainer將管理零件的使用期限,在這種情況下,CreationPolicy.Shared(這是導出類型的默認值)使用單生命週期策略。然後您可以導入IFormPartCustomator的實例,導入相同的單例實例。

我還認爲將其稱爲Factory可能是錯誤的,因爲工廠設計用於創建新實例,而您的類型只會創建每個ICustomRenderer的一個實例。如果這是預期的行爲,那麼可能會更好地稱爲FormPartCustomatorService,它實現了IFormPartCusomatorService接口?如果你想每次啓動新的實例,你可以看看ExportFactory<ICustomRenderer, ICustomRendererMetadata>

+0

謝謝,這是一個很好的答案。 – Peter

9

正如馬修提到的,SatisfyImportsOnce沒有註冊重組部分。這意味着MEF容器不包含對零件的引用。

另外,SatisfyImportsOnce只滿足零件的進口,但忽略它的任何出口。 ComposeParts也會將導出添加到容器中。

+0

謝謝丹尼爾。非常有幫助 – Peter

+2

「ComposeParts也會將出口添加到容器中。」 - 這就是我一直在尋找的:) –

相關問題