2012-05-22 4 views
7

Я изучаю MEF, и я хотел создать простой пример (приложение), чтобы увидеть, как он работает в действии. Таким образом, я подумал о простом переводчике. Я создал решение с четырьмя проектами (DLL-файлы):Правильное использование атрибута [Import] в MEF

Контракты
Веб
BingTranslator
GoogleTranslator

Контракты содержит интерфейс ITranslate. Поскольку имя применяется, оно будет содержать только контракты (интерфейсы), поэтому экспортеры и импортеры могут его использовать.

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

BingTranslator и GoogleTranslator являются экспортерами этого договора. Они оба реализуют этот контракт и предоставляют (экспортируют) различные услуги перевода (один из Bing, другой из 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 является:

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

Теперь в моем проекте Web, я просто хочу, чтобы получить текст от пользователя, перевести его с одним из этих переводчиков (Bing и Google), и вернуть результат обратно пользователю. Таким образом, в приложении Web я зависим от переводчика. Поэтому я создал контроллер, таким образом:

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

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

и последний кусок головоломки должна быть склеить эти компоненты (части) вместе (чтобы составить общую композицию из небольших кусочков). Так, в Application_Start из Web проекта, у меня есть:

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

, в котором /parts папке, в которой я падаю GoogleTranslator.dll и BingTranslator.dll файлов (экспортеры расположены в этих файлах) , а в папке /bin У меня просто есть файл Web.dll, содержащий импортер. Однако моя проблема заключается в том, что MEF не заполняет Translator свойство GeneralController с необходимым переводчиком. Я читал почти каждый вопрос, связанный с MEF на этом сайте, но я не мог понять, что не так с моим примером. Может кто-нибудь, пожалуйста, скажите мне, что я пропустил здесь?

ответ

9

ОК, что вам нужно сделать, это (без предписания для исполнения, это только, чтобы он работал)

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

Я не эксперт в MEF, и быть откровенным за то, что я использую он для, он не делает много для меня, так как я использую его только для загрузки DLL, а затем у меня есть точка входа в зависимость, а с тех пор я использую контейнеры DI, а не MEF.

MEF является обязательным - насколько я видел. В вашем случае вам необходимо проактивно составить то, что вам нужно, чтобы быть MEFed, т. Е. ваш контроллер.Таким образом, вашей фабрике контроллеров необходимо составить экземпляр вашего контроллера.

Поскольку я редко использую компоненты Мефеда в моем приложении MVC, у меня есть фильтр для тех действий, требующих MEF (вместо MEFing всех моих контроллеров в моем контроллере facrory):

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

Здесь UniversalCompositionContainer.Current.Container одноэлементно контейнер, инициализированный каталогими каталогов.


Мой личный взгляд на MEF

MEF, а не рамки DI, он делает много что. Таким образом, существует большое перекрытие с DI и , если вы уже используете каркас DI, они обязательно столкнутся с.

MEF очень эффективен при загрузке DLL во время выполнения, особенно когда у вас есть приложение WPF, где вы можете загружать/выгружать плагины и ожидать, что все остальное будет работать так, как было, добавив/удалив функции.

Для веб-приложения это не имеет большого смысла, поскольку на самом деле вы не должны бросать DLL в рабочее веб-приложение. Следовательно, его использование очень ограничено.

Я собираюсь написать сообщение о плагинах в ASP.NET MVC и обновить это сообщение ссылкой.

+0

Благодарим за ответ @Aliostad, но, честно говоря, я не понял, что мне следует сделать, чтобы '[Import]' работал с моим свойством «Переводчик». –

+0

@SaeedNeamati OK, я обновил, чтобы продемонстрировать, как его использовать. – Aliostad

+3

Ну, вот ваш коллега для вашего взгляда - MEF является частью .NET и довольно хорошей картой DI сама по себе. Использование другого в большинстве случаев не оправдано и просто вводит технологию ANOTHER без усиления (т. Е. Значение обслуживания отрицательное). Просто закончить 18-месячный проект ТОЛЬКО с помощью MEF;) Работал неплохо. – TomTom

2

Как указано в @Aliostad, вам нужно иметь код инициализации композиции, запускаемый во время/после создания контроллера, чтобы он работал - просто его наличие в файле global.asax не будет работать.

Однако вам необходимо будет использовать [ImportMany] вместо [Import], так как в вашем примере вы можете работать с любым количеством реализаций ITranslator из двоичных файлов, которые вы обнаружите. Дело в том, что если у вас много ITranslator, но импортируют их в один экземпляр, скорее всего, вы получите исключение из MEF, так как он не будет знать, какую реализацию вы действительно хотите.

Так вместо того, чтобы использовать:

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

Быстрый пример:

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

5

MEF только заселить импорта на объекты, которые он создает сам. В случае ASP.NET MVC ASP.NET создает объекты контроллера. Он не распознает атрибут [Import], поэтому вы видите, что зависимость отсутствует.

Для того, чтобы построить MEF контроллеры, вы должны сделать следующее:

  1. Марк класс контроллер сам с [Export].
  2. Внедрение реализации IDependencyResolver, которая обертывает контейнер MEF. Вы можете реализовать GetService, запросив контейнер MEF для соответствующего экспорта. Вы можете создать строку контракта MEF из запрошенного типа с помощью AttributedModelServices.GetContractName.
  3. Зарегистрировать этот резольвер, позвонив по телефону DependencyResolver.SetResolver в Application_Start.

Возможно, вам также необходимо отметить большую часть экспортируемых частей [PartCreationPolicy(CreationPolicy.NonShared)], чтобы предотвратить повторное использование одного и того же экземпляра в нескольких запросах одновременно. Любое государство, содержащееся в ваших частях MEF, в противном случае будет подвергнуто условиям гонки.

Редактировать: этот blog post имеет хороший пример всей процедуры.

изменить2: может возникнуть другая проблема. Контейнер MEF будет содержать ссылки на любой объект IDisposable, который он создает, чтобы он мог уничтожать эти объекты, когда сам контейнер находится. Однако это не подходит для объектов с продолжительностью жизни «на запрос»! У вас действительно будет утечка памяти для любых служб, которые реализуют IDisposable.

Возможно, проще использовать альтернативу, например AutoFac, которая имеет пакет NuGet для ASP.NET MVC integration и имеет поддержку для per-request lifetimes.

+1

+1. MEF не был разработан как DI Framework, поэтому его использование для DI было настолько сложным - хорошо изначально разработанным для VS-плагинов. Все расположение службы поддержки фрейма DI передают экземпляр типа. – Aliostad

+0

@Aliostad: Оказывается, это не проблема, потому что контракты MEF являются фактически строками, которые вы можете генерировать из этого типа. Я уточню свой ответ. –

+0

MEF отлично, но поистине глупо добавлять не-семантические атрибуты к типу, который вообще не предоставляет никаких сервисов. Я имею в виду, что, если мой контроллер не предоставляет (экспортирует) какую-либо услугу? Должен ли я всегда украшать его атрибутом '[Export]'? Если это так, я предпочитаю использовать другой подход. –

Смежные вопросы