2012-05-22 17 views
7

Estoy aprendiendo MEF y quería crear un ejemplo simple (aplicación) para ver cómo funciona en acción. Por lo tanto, pensé en un simple traductor. He creado una solución con cuatro proyectos (archivos DLL):Uso adecuado del atributo [Importar] en MEF

contratos
Web
Bing Translator
GoogleTranslator

contratos contiene la interfaz ITranslate. Como se aplica el nombre, solo contendría contratos (interfaces), por lo que los exportadores e importadores pueden usarlo.

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

Bing Translator y GoogleTranslator son ambos exportadores de este contrato. Ambos implementan este contrato y proporcionan (exportan) diferentes servicios de traducción (uno de Bing, otro de 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"; 
    } 
} 

y la BingTranslator es:

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

Ahora, en mi proyecto Web, simplemente quiero obtener el texto del usuario, traducirlo con uno de los traductores (Bing y Google), y devolver el resultado al usuario. Por lo tanto, en mi aplicación Web, dependo de un traductor. Por lo tanto, he creado un controlador de esta manera:

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

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

y la última pieza del rompecabezas debe ser para pegar estos componentes (partes) juntos (para componer la canción en general a partir de piezas más pequeñas). Así, en Application_Start del proyecto Web, que tengo:

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

en el que /parts es la carpeta en la que se me cae GoogleTranslator.dll y BingTranslator.dll archivos (exportadores se encuentran en estos archivos) , y en la carpeta /bin simplemente tengo mi Web.dll archivo que contiene el importador. Sin embargo, mi problema es que MEF no puebla Translator propiedad del GeneralController con el traductor requerido. Leí casi todas las preguntas relacionadas con MEF en este sitio, pero no pude entender cuál es el problema con mi ejemplo. ¿Alguien puede decirme qué he perdido aquí?

Respuesta

9

Aceptar lo que hay que hacer es (sin prescripción para el rendimiento, esto es sólo para ver su funcionamiento)

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

No soy experto en el MEF, y para ser franco para lo que yo uso para mí, no hace mucho para mí ya que solo lo uso para cargar archivos DLL y luego tengo un punto de entrada para la inyección de la dependencia y de ahí en adelante utilizo contenedores DI y no MEF.

MEF es imprescindible, por lo que he visto. En su caso, debe componer proactivamente lo que necesita para ser MEFed, es decir, su controlador.Por lo tanto, su fábrica de controladores debe componer su instancia de controlador.

Ya que rara vez utilizan componentes MEFed en mi MVC aplicación, tengo un filtro para aquellas acciones que requieren MEF (en lugar de MEFing todos mis controladores en mi regulador 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); 
    } 
} 

Aquí UniversalCompositionContainer.Current.Container es un producto único contenedor inicializado con mis catálogos de directorios.


Mi opinión personal sobre MEF

MEF, aunque no es un marco DI, que hace mucho de eso. Como tal, hay una gran superposición con DI y si ya utiliza el marco DI, están destinados a colisionar.

MEF es potente en la carga de archivos DLL en tiempo de ejecución, especialmente cuando tienes la aplicación WPF, donde puedes cargar/descargar complementos y esperar que todo funcione igual que antes, añadiendo/eliminando características.

Para una aplicación web, esto no tiene mucho sentido, ya que realmente no se supone que debes soltar una DLL en una aplicación web en funcionamiento. Por lo tanto, sus usos son muy limitados.

Voy a escribir una publicación sobre complementos en ASP.NET MVC y actualizaré esta publicación con un enlace.

+0

Gracias por responder a @Aliostad, pero francamente no entendí qué debería hacer para que '[Import]' funcione en mi propiedad 'Translator'. –

+0

@SaeedNeamati Bien, lo he actualizado para demostrar cómo usarlo. – Aliostad

+3

Bueno, aquí está la contraparte de su vista: MEF es parte de .NET y un marco de DI bastante bueno en sí mismo. El uso de otro en la mayoría de los casos no está justificado y simplemente introduce OTRA tecnología sin ganancia (es decir, valor de mantenimiento negativo). Acabo de terminar un proyecto de 18 meses SOLAMENTE usando MEF;) Funcionó bastante bien. – TomTom

2

Como se menciona en @Aliostad, necesita tener el código de inicialización de la composición ejecutándose durante/después de la creación del controlador para que funcione; simplemente, tenerlo en el archivo global.asax no funcionará.

Sin embargo, también necesitará usar [ImportMany] en lugar de solo [Import], ya que en su ejemplo podría estar trabajando con cualquier cantidad de implementaciones de ITRAlator de los binarios que descubra. El punto es que si tiene muchos ITranslator, pero los está importando en una sola instancia, es probable que obtenga una excepción de MEF, ya que no sabrá qué implementación realmente desea.

Así que en vez que utilice:

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

Ejemplo rápido:

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

5

MEF sólo se rellenará las importaciones en los objetos que se construye a sí misma. En el caso de ASP.NET MVC, es ASP.NET el que crea los objetos del controlador. No reconocerá el atributo [Import], por eso es que ve que falta la dependencia.

Para hacer MEF construcción de los controladores, usted tiene que hacer lo siguiente:

  1. marca de clase del controlador mismo con [Export].
  2. Implemente una implementación IDependencyResolver que envuelva el contenedor MEF. Puede implementar GetService preguntando al contenedor MEF por una exportación coincidente. Puede generar una cadena de contrato MEF del tipo solicitado con AttributedModelServices.GetContractName.
  3. Registre esa resolución llamando al DependencyResolver.SetResolver en Application_Start.

Probablemente también tienen que marcar la mayor parte de sus piezas exportadas con [PartCreationPolicy(CreationPolicy.NonShared)] para evitar que la misma instancia de ser reutilizado en varias solicitudes al mismo tiempo. Cualquier estado mantenido en sus partes MEF estaría sujeto a condiciones de carrera de lo contrario.

editar: este blog post tiene un buen ejemplo de todo el procedimiento.

edit2: puede haber otro problema. El contenedor MEF guardará referencias a cualquier objeto IDisposable que cree, de modo que pueda disponer de esos objetos cuando el propio contenedor esté dispuesto. Sin embargo, esto no es apropiado para objetos con una vida útil "por solicitud". Efectivamente tendrá una pérdida de memoria para cualquier servicio que implemente IDisposable.

Probablemente sea más fácil simplemente usar una alternativa como AutoFac, que tiene un paquete NuGet para ASP.NET MVC integration y que tiene soporte para per-request lifetimes.

+1

+1. MEF no fue diseñado como un Marco DI, por lo tanto, usarlo para DI es muy complicado, originalmente desarrollado para plugins VS. Toda la ubicación del servicio de soporte de marcos DI pasando la instancia de tipo. – Aliostad

+0

@Aliostad: Resulta que esto no es realmente un problema porque los contratos MEF son en realidad cadenas que puede generar usted mismo del tipo. Actualizaré mi respuesta. –

+0

MEF es genial, pero es realmente tonto agregar atributos no semánticos a un tipo que no ofrece ningún servicio. Quiero decir, ¿qué pasa si mi controlador no proporciona (exporta) ningún servicio? ¿Debo decorarlo siempre con el atributo '[Exportar]'? Si es así, prefiero usar otro enfoque. –

Cuestiones relacionadas