2012-05-22 28 views
7

Tôi đang học MEF và tôi muốn tạo một ví dụ đơn giản (ứng dụng) để xem nó hoạt động như thế nào. Vì vậy, tôi nghĩ về một dịch giả đơn giản. Tôi tạo ra một giải pháp với bốn dự án (file DLL):Việc sử dụng đúng thuộc tính [Nhập] trong MEF

Hợp đồng
Web
BingTranslator
GoogleTranslator

Hợp đồng chứa các giao diện ITranslate. Như tên áp dụng, nó sẽ chỉ chứa các hợp đồng (giao diện), do đó các nhà xuất khẩu và nhập khẩu có thể sử dụng nó.

public interface ITranslator 
{ 
    string Translate(string text); 
} 

BingTranslatorGoogleTranslator đều xuất khẩu của hợp đồng này. Cả hai đều thực hiện hợp đồng này và cung cấp (xuất) các dịch vụ dịch thuật khác nhau (một từ Bing, một từ Google).

[Export(typeof(ITranslator))] 
public class GoogleTranslator: ITranslator 
{ 
    public string Translate(string text) 
    { 
     // Here, I would connect to Google translate and do the work. 
     return "Translated by Google Translator"; 
    } 
} 

BingTranslator là:

[Export(typeof(ITranslator))] 
public class BingTranslator : ITranslator 
{ 
    public string Translate(string text) 
    { 
     return "Translated by Bing"; 
    } 
} 

Bây giờ, trong dự án Web của tôi, tôi chỉ đơn giản muốn nhận được văn bản từ người dùng, dịch nó với một trong những dịch giả (Bing và Google) và trả lại kết quả cho người dùng. Do đó, trong ứng dụng Web của tôi, tôi phụ thuộc vào người dịch. Do đó, tôi đã tạo một bộ điều khiển theo cách này:

public class GeneralController : Controller 
{ 
    [Import] 
    public ITranslator Translator { get; set; } 

    public JsonResult Translate(string text) 
    { 
     return Json(new 
     { 
      source = text, 
      translation = Translator.Translate(text) 
     }); 
    } 
} 

và phần cuối cùng của câu đố là dán các thành phần này (để soạn toàn bộ bài hát từ các phần nhỏ hơn). Vì vậy, trong Application_Start của dự án Web, tôi có:

 var parts = new AggregateCatalog 
      (
       new DirectoryCatalog(Server.MapPath("/parts")), 
       new DirectoryCatalog(Server.MapPath("/bin")) 
      ); 
     var composer = new CompositionContainer(parts); 
     composer.ComposeParts(); 

trong đó /parts là thư mục nơi tôi thả GoogleTranslator.dllBingTranslator.dll file (nhà xuất khẩu đang nằm trong những tập tin này) và trong thư mục /bin Tôi chỉ cần có tệp Web.dll chứa nhà nhập khẩu của mình. Tuy nhiên, vấn đề của tôi là, MEF không điền thuộc tính Translator của GeneralController với trình dịch yêu cầu. Tôi đã đọc hầu hết mọi câu hỏi liên quan đến MEF trên trang web này, nhưng tôi không thể tìm ra điều gì sai với ví dụ của mình. Bất cứ ai có thể vui lòng cho tôi biết những gì tôi đã bỏ lỡ ở đây?

Trả lời

9

OK những gì bạn cần làm là (không có quy định để thực hiện, điều này chỉ là để xem nó làm việc)

public class GeneralController : Controller 
{ 
    [Import] 
    public ITranslator Translator { get; set; } 

    public JsonResult Translate(string text) 
    { 
     var container = new CompositionContainer(
     new DirectoryCatalog(Path.Combine(HttpRuntime.BinDirectory, "Plugins"))); 
     CompositionBatch compositionBatch = new CompositionBatch(); 
     compositionBatch.AddPart(this); 
     Container.Compose(compositionBatch); 

     return Json(new 
     { 
      source = text, 
      translation = Translator.Translate(text) 
     }); 
    } 
} 

Tôi không có chuyên môn trong MEF, và để được thẳng thắn cho những gì tôi sử dụng nó cho, nó không làm nhiều cho tôi kể từ khi tôi chỉ sử dụng nó để tải DLLs và sau đó tôi có một điểm nhập cảnh để phụ thuộc tiêm và từ đó tôi sử dụng DI container và không MEF.

MEF là bắt buộc - theo như tôi đã thấy. Trong trường hợp của bạn, bạn cần phải chủ động soạn những gì bạn cần phải được MEFED, tức là bộ điều khiển của bạn.Vì vậy, nhà máy điều khiển của bạn cần phải soạn thể hiện điều khiển của bạn.

Vì tôi hiếm khi sử dụng các thành phần MEFed trong ứng dụng MVC của tôi, tôi có một bộ lọc đối với những hành động đòi hỏi MEF (thay vì MEFing tất cả các bộ điều khiển của tôi trong facrory điều khiển của tôi):

public class InitialisePluginsAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     CompositionBatch compositionBatch = new CompositionBatch(); 
     compositionBatch.AddPart(filterContext.Controller); 
     UniversalCompositionContainer.Current.Container.Compose(
      compositionBatch); 
     base.OnActionExecuting(filterContext); 
    } 
} 

Đây UniversalCompositionContainer.Current.Container là một singleton container được khởi tạo với danh mục thư mục của tôi.


quan điểm cá nhân của tôi trên MEF

MEF, trong khi không phải là một khuôn khổ DI, nó hoạt động rất nhiều về điều đó. Như vậy, có một sự chồng chéo lớn với DI và nếu bạn đã sử dụng khung DI, chúng nhất định sẽ va chạm.

MEF mạnh mẽ trong việc tải tệp DLL trong thời gian chạy đặc biệt khi bạn có ứng dụng WPF nơi bạn có thể tải/dỡ plugin và mong đợi mọi thứ khác hoạt động như vậy, thêm/xóa tính năng.

Đối với một ứng dụng web, điều này không có nhiều ý nghĩa, vì bạn thực sự không phải bỏ một DLL trong một ứng dụng web đang hoạt động. Do đó, việc sử dụng nó rất hạn chế.

Tôi sẽ viết một bài đăng trên các plugin trong ASP.NET MVC và sẽ cập nhật bài đăng này bằng một liên kết.

+0

Cảm ơn bạn đã trả lời @Aliostad, nhưng thẳng thắn tôi không hiểu tôi nên làm gì để làm cho '[Import]' hoạt động trên thuộc tính 'Translator' của tôi. –

+0

@SaeedNeamati OK, tôi đã cập nhật để minh họa cách sử dụng nó. – Aliostad

+3

Vâng, đây là đối tác cho quan điểm của bạn - MEF là một phần của .NET và một khung DI khá tốt trong chính nó. Việc sử dụng một số khác là trong hầu hết các trường hợp không được chứng minh và chỉ giới thiệu công nghệ KHÁC mà không đạt được (tức là giá trị bảo trì âm). Chỉ cần hoàn thành một dự án 18 tháng CHỈ bằng MEF;) Làm việc khá tốt đẹp. – TomTom

2

Như @Aliostad đã đề cập, bạn cần phải có mã khởi tạo thành phần chạy trong/sau khi tạo bộ điều khiển để nó hoạt động - chỉ cần có nó trong tệp global.asax sẽ không hoạt động.

Tuy nhiên, bạn cũng sẽ cần sử dụng [ImportMany] thay vì chỉ [Import], vì trong ví dụ của bạn, bạn có thể làm việc với bất kỳ số lần triển khai ITranslator nào từ các tệp nhị phân mà bạn khám phá. Vấn đề là nếu bạn có nhiều ITranslator, nhưng đang nhập chúng vào một trường hợp duy nhất, bạn có thể sẽ có ngoại lệ từ MEF vì nó sẽ không biết bạn thực sự muốn triển khai thực hiện điều gì.

Vì vậy, thay vào đó bạn sử dụng:

[ImportMany] 
public IEnumerable<ITranslator> Translator { get; set; } 

nhanh Ví dụ:

http://dotnetbyexample.blogspot.co.uk/2010/04/very-basic-mef-sample-using-importmany.html

5

MEF sẽ chỉ cư nhập khẩu trên các đối tượng mà nó xây dựng chính nó. Trong trường hợp của ASP.NET MVC, nó là ASP.NET mà tạo ra các đối tượng điều khiển. Nó sẽ không nhận ra thuộc tính [Import], vì vậy đó là lý do tại sao bạn thấy rằng phụ thuộc bị thiếu.

Để làm MEF xây dựng bộ điều khiển, bạn phải làm như sau:

  1. Đánh dấu bộ điều khiển lớp bản thân với [Export].
  2. Triển khai thực hiện IDependencyResolver kết thúc tốt đẹp với vùng chứa MEF. Bạn có thể triển khai GetService bằng cách yêu cầu vùng chứa MEF cho một lần xuất phù hợp. Bạn có thể tạo chuỗi hợp đồng MEF từ loại được yêu cầu với AttributedModelServices.GetContractName.
  3. Đăng ký người giải quyết đó bằng cách gọi DependencyResolver.SetResolver trong Application_Start.

Bạn cũng có thể cần đánh dấu hầu hết các phần đã xuất của mình với [PartCreationPolicy(CreationPolicy.NonShared)] để ngăn đồng thời sử dụng lại một số yêu cầu trong cùng một yêu cầu. Bất kỳ trạng thái nào được lưu giữ trong các bộ phận MEF của bạn sẽ phải tuân theo các điều kiện chủng tộc khác.

chỉnh sửa: điều này blog post có ví dụ điển hình về toàn bộ quy trình.

edit2: có thể có sự cố khác. Thùng chứa MEF sẽ giữ tham chiếu đến bất kỳ đối tượng IDisposable nào mà nó tạo ra, để nó có thể loại bỏ các đối tượng đó khi chính vùng chứa đó được xử lý. Tuy nhiên, điều này không thích hợp cho các đối tượng có thời gian "theo yêu cầu"! Bạn sẽ có hiệu quả rò rỉ bộ nhớ cho bất kỳ dịch vụ nào thực hiện IDisposable.

Có lẽ dễ dàng hơn khi chỉ sử dụng phương án thay thế như AutoFac, có gói NuGet cho ASP.NET MVC integration và có hỗ trợ cho per-request lifetimes.

+1

+1. MEF không được thiết kế như một khung DI, do đó việc sử dụng nó cho DI rất phức tạp - ban đầu được phát triển cho các plugin VS. Tất cả vị trí dịch vụ hỗ trợ DI framework bằng cách truyền thể hiện kiểu. – Aliostad

+0

@Aliostad: Nó chỉ ra rằng điều này không thực sự là một vấn đề bởi vì các hợp đồng MEF thực sự là chuỗi mà bạn có thể tạo ra chính mình từ loại. Tôi sẽ cập nhật câu trả lời của mình. –

+0

MEF là tuyệt vời, nhưng nó thực sự ngớ ngẩn để thêm thuộc tính không ngữ nghĩa vào một loại mà không cung cấp dịch vụ nào cả. Ý tôi là, nếu bộ điều khiển của tôi không cung cấp (xuất) bất kỳ dịch vụ nào? Tôi có nên luôn trang trí nó với thuộc tính '[Xuất] 'không? Nếu vậy, tôi thích sử dụng cách tiếp cận khác. –

Các vấn đề liên quan