2011-06-21 20 views
14

Ai đó có thể giải thích sự khác biệt giữa SatisfyImportsOnceComposeParts và tại sao một người sẽ làm việc ở nơi khác không?SatisfyImportsOnce vs ComposeParts

Cụ thể tôi có ứng dụng Web MVC mà tôi đang sử dụng MEF. Dưới đây là một số mã (từ ứng dụng đó) hoạt động khi tôi sử dụng SatisfyImportsOnce nhưng không sử dụng khi tôi sử dụng ComposeParts. Hiểu biết của tôi là ComposeParts tạo các bộ phận có thể ghép từ một mảng các đối tượng được phân bổ và soạn chúng trong thùng chứa thành phần được chỉ định và rằng SatisfyImportsOnce soạn phần được chỉ định bằng cách sử dụng dịch vụ thành phần được chỉ định. Với bộ não khỉ đơn giản của tôi mặc dù tiếng Anh khác nhau nhưng chúng giống nhau về mặt ngữ nghĩa. Cả hai đều sử dụng CompositionContainer để nhổ các loại được xuất tại mục tiêu nhập.

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; 
    } 
} 

Trả lời

20

SatisyImportsOnce sẽ soạn một loại mà không cần đăng ký để khôi phục. Vì vậy, nếu bạn định sử dụng loại mà không hỗ trợ cho việc phân phối, bạn có thể sử dụng SatisfyImportsOnce và nó sẽ thực hiện công việc như bình thường, nhưng bất kỳ thay đổi nào trong vùng chứa (phần mới được thêm vào hoặc phần đã bị xóa), thì trường hợp của bạn sẽ không tự động được sắp xếp lại để cung cấp những phần mới này.

Trong trường hợp của bạn, bạn đang sử dụng:

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

... nhưng qua SatisfyImportsOnce, nhập khẩu này sẽ không được recomposed.

Nếu bạn không phải lo lắng về recomposition, bạn có thể thay đổi constructor injection sử dụng mã của bạn, vì vậy bạn có thể làm:

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

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

Lý do tôi xin đề nghị constructor injection, là tập hợp của Lazy<ICustomRenderer, ICustomRendererMetadata> trường là một phụ thuộc rõ ràng mà loại của bạn yêu cầu, vì vậy sẽ tốt hơn khi khởi tạo loại của bạn ở trạng thái có thể sử dụng, thay vì khởi tạo và sau đó yêu cầu một bước bổ sung để chuẩn bị sẵn sàng cho lần sử dụng đầu tiên.

Điều này khiến cho loại FormPartCustomatorFactory của bạn có thể kiểm tra được nhiều hơn. Để kết thúc này, nếu bạn đã thay đổi các nhà xây dựng như vậy, sau đó phương pháp của bạn làm cho nó một singleton sẽ không hoạt động. Thay vào đó, bạn có thể tận dụng lợi thế của các chức năng quản lý vòng đời của MEF, vì vậy có thể thiết kế lại kiểu của bạn như:

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(); 
    } 
} 

Làm theo cách này có nghĩa là loại của bạn không phụ thuộc vào MEF, nó có thể được sử dụng mà không có nó, nó dễ kiểm tra hơn và CompositionContainer sẽ quản lý toàn bộ thời gian của bộ phận, trong trường hợp này là CreationPolicy.Shared (là mặc định cho các loại được xuất), sử dụng chiến lược toàn thời gian đơn. Sau đó, bạn có thể nhập một phiên bản của IFormPartCustomator, bạn nhập cùng một cá thể đơn lẻ.

Tôi cũng cho rằng gọi số Factory có thể sai, vì nhà máy được thiết kế để tạo các phiên bản mới, trong khi đó, loại của bạn sẽ chỉ tạo một phiên bản của mỗi ICustomRenderer. Nếu đây là hành vi dự định, có lẽ nó sẽ tốt hơn được gọi là FormPartCustomatorService, thực hiện giao diện IFormPartCusomatorService? Nếu bạn muốn xoay vòng các phiên bản mới mỗi lần, bạn có thể xem ExportFactory<ICustomRenderer, ICustomRendererMetadata>.

+0

Cảm ơn bạn, đây là một câu trả lời tuyệt vời. – Peter

9

Như Matthew đề cập, SatisfyImportsOnce không đăng ký một phần để khôi phục. Điều này có nghĩa là các container MEF không giữ một tham chiếu đến phần.

Ngoài ra, SatisfyImportsOnce chỉ đáp ứng việc nhập khẩu một phần, nhưng bỏ qua bất kỳ xuất khẩu nào có. ComposeParts cũng sẽ thêm xuất khẩu vào vùng chứa.

+0

Cảm ơn Daniel. rất hữu ích – Peter

+2

"ComposeParts cũng sẽ thêm xuất khẩu vào vùng chứa." - đây là những gì tôi đang tìm :) –