Introducción al Managed Extensibility Framework en .NET 4.0

Introducción al Managed Extensibility Framework en .NET 4.0
IntroductionMEF

¡Hola geeks!

Hoy vamos a aprender a utilizar una herramienta bastante interesante si pensáis construir aplicaciones grandes. Sirve para crear aplicaciones extensibles y está disponible a partir de la versión .NET 4.0. Por supuesto, como el código está disponible, podéis descargarlo y compilarlo para .NET 3.5 si así lo necesitáis.

¿Qué significa tener una aplicación extensible?

Explicado coloquialmente, lo que conseguimos con MEF, es que nuestra aplicación pueda incorporar/importar proyectos hechos por desarrolladores externos o de la misma compañía en nuestra app, si cumple nuestra implementación de Interfaces. Resumiendo un poco más, con MEF puedes abrir unos "puertos" para que desarrolladores pueden implementar extensiones para tu app. Por su puesto, éste método también se utiliza dentro de la misma producción de una app para así organizar mejor el trabajo/tareas y dicha implementación.

Introducción a MEF

Quiero dar primero un enfoque teórico ya que lo veo necesario para entender cómo funciona MEF. ¡Así que no te pases ya a los ejemplos! que nos conocemos :D

Necesidades que llevan a utilizar MEF

En los tiempo que corren, los desarrolladores ocupamos más tiempo modificando existentes proyectos que una nuevo desde cero. Esto se debe a que la tecnología avanza a un ritmo de vértigo, donde tu proyecto puede quedarse detrás en un plazo corto. Dicha situación ocurre por los constantes cambios de los requerimientos, funcionalidades y objetivos a nivel del proyecto, empresa o que simplemente los usuarios piden ese cambio. Para estas situaciones, es importante estar preparados y agregarlo en menos tiempo posible.

Concepto de funcionamiento

Para tener el código flexible y propenso a cambios, para estar preparados a dichos cambios mencionados arriba, explicamos cómo podría ser el concepto.

Supongamos que cada funcionalidad de la aplicación pueda ser descompuesta en una "parte". Lo que necesitamos es la capacidad de desarrollar partes débilmente acopladas, independiente no sólo en su estructura sino también en su prueba y en su distribución. Con prueba, me refiero a poder testear dicha funcionalidad en su proyecto sin necesidad de estar acoplada al proyecto raíz. Es importante también que esas partes pueden ser fácilmente compuestas en una aplicación.

Siguientes roles dentro de la estructura MEF

  • Partes exportadas. Es el proyecto/unidad independiente de desarrollo, compilación y distribución donde se declaran, que pueden ser usadas para componer una aplicación y el contrato que implementan. Tienen que estar débilmente acoplados, donde no tienen por que si conocerse entre sí.
  • Puntos de importación. Variables donde importamos las partes o colecciones de partes. Dichas partes deben importar cierto contrato, son creadas automáticamente a partir de la información contenido en catálogos de partes.
  • Catálogo de partes. Contienen definiciones de partes: donde están y qué contrato implementan.
  • Contenedores de partes. Contienen partes instanciadas y realizan la composición de la partes.

Aquí podemos ver una estructura de MEF:

MEF-Struktur

Su núcleo está constituido por "Catalog" y "Composition Container". Catalog es responsable por el descubrimiento de las extensiones en su aplicación. Composition Container es responsable por la satisfacción de las dependencias. Catalog permite consumir las "exports" de las aplicaciones registradas mediante los atributos de exportación, mientras CompositionContainer expone métodos para estas exportaciones en nuestras aplicaciones.

Vista general de la Arquitectura.

MEF se puede dividir en tres capas. El nivel de "Container" es donde pone a disposición del usuario la mayor parte de la API.

Los elementos básicos que una "Layer of indirection" ofrece es que MEF no esté estrechamente relacionado con los imports/exports. Tienen una implementación predeterminada en la capa primitiva, a lo que llamamos "Attributed Programming Model" que se basa en los tipos y atributos para el descubrimiento y la definición de imports/exports.

MEFarchitectur1

Ejemplo de MEF

Para comenzar, creamos un nuevo proyecto y le añadimos como referencia "System.ComponentModel.Composition“.

Ahora creamos un Interface con un método. Es quien va a definir el contrato que tienen que cumplir las extensiones para que en mi proyecto sea aceptado. public interface IPerson { void WriteMessage(); } Ahora vamos a implementar nuestra Interface en unas Clases que serán los extensibles de nuestra aplicación. Creamos dos clases con nombres "Customer y "Mitarbeiter" quienes implementan IPerson con su método. public class Customer : IPerson { public void WriteMessage() { Console.WriteLine("Soy un Customer"); Console.ReadLine(); } } public class Employee : IPerson { public void WriteMessage() { Console.WriteLine("Soy un Employee"); Console.ReadLine(); } } Una vez implementada la lógica que definía el extensible, es hora que entre en juego la referencia que añadimos al principio. Para asignar la etiqueta como una clase es exportable, basta con marcar dicha clase con Export: [Export(typeof(IPerson))] public class Customer : IPerson { public void WriteMessage() { Console.WriteLine("Soy un Customer"); Console.ReadLine(); } } [Export(typeof(IPerson))] public class Employee : IPerson { public void WriteMessage() { Console.WriteLine("Soy un Employee"); Console.ReadLine(); } }

Ahora que sabemos las clases que son exportables, necesitamos construir una infraestructura en nuestra App para poder implementarlas. Necesitamos programar la composición de las partes, un catálogo que describa las partes.

Aquí podemos definir de dónde debe coger los proyectos (conocidos como Plug-Ins) para importarlos.

AssemblyCatalog catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());

Aquí en vez de buscar en una carpeta, lo que hacemos es buscar dentro de nuestro código actual. Donde hemos creado las clases. Si hubiéramos creado extra clases para cada Plugin, necesitaríamos una carpeta donde buscar.

También necesitamos un contenedor para las instancias de los extensibles/partes y para crear la composición. Le pasamos el catálogo para que tenga la info de los plugins.

CompositionContainer compositionContainer = new CompositionContainer(catalog); compositionContainer.ComposeParts(this);

Lo que ahora nos falta, es un punto donde importar los plugins. Es importante crear una variable o propiedad con la misma Interface con el que fueron implementados los extensibles.

[ImportMany] public IPerson[] Persons { get; set; }

Al colocar ImportMany, le indicamos a MEF que en esta propiedad que recibirá más de un Plugin con ésta Interface.

Por último, lo que la App hace es coger los métodos de WriteMessage de cada plugin y los muestra por consola:

class Program { [ImportMany] public IPerson[] Persons { get; set; } static void Main(string[] args) { Program p = new Program(); p.ListAllPersons(); } private void ListAllPersons() { CreateCompositionContainer(); foreach (var person in Persons) { person.WriteMessage(); } } private void CreateCompositionContainer() { AssemblyCatalog catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); CompositionContainer compositionContainer = new CompositionContainer(catalog); compositionContainer.ComposeParts(this); } }

Pues hasta aquí hemos llegado. Para ampliar más información os dejo un artículo de interés.

¡Gracias por leer y hasta la próxima!