.NET - Apresentando o MEF - Microsoft Extensibility Framework
O Microsoft Extensibility Framework (MEF) é um framework que foi introduzido na versão 4.0 do .NET Framework e também no SilverLight 4.0 e tem como objetivo principal permitir a criação e o gerenciamento de aplicações extensíveis.
Você já ouviu o jargão : "Trocar a roda com o carro andando..."
Pois é justamente isso que a utilização do MEF permite : que você possa estender sua aplicação em tempo de execução sem ter que recompilar toda a aplicação ou ter que redesenhá-la novamente.
Todos nós sabemos que um produto de software sofre constantes mudanças durante o seu ciclo de vida. Para minimizar os impactos dessas mudanças é aconselhável que a aplicação esteja aderente a certos princípios de arquitetura como:
O quesito extensibilidade é muito importante, pois sua adoção permite que você defina que partes de sua aplicação sejam extensíveis, de forma que essas funcionalidades possam ser substituídas em tempo de execução por outras implementações da mesma funcionalidade seja através de componentes de terceiros, plugins, etc.
Para dar suporte a uma arquitetura extensível você precisa definir em sua aplicação os pontos de extensibilidade que, em caso de necessidade, poderão ser substituídos em tempo de execução por implementações diferentes. Por exemplo substituir a implementação do cache por outra.
Naturalmente isso deve ser feito com o menor custo possível e sem impactar a sua aplicação como um todo, ou seja, isso tem que ser feito sem você ter que alterar o código ou ter que recompilar a sua aplicação.
Dependendo da complexidade envolvida na sua aplicação isso pode dar muito trabalho e por isso existem alguns frameworks que nos ajudam a realizar esta tarefa como : Unity Framework, Structure Map, Mono.Addins, Castle Windsor, etc.
O MEF é portanto mais um framework que podemos usar e ele apresenta as seguintes vantagens :
E como o MEF funciona ?
Vou descrever um cenário básico retirado do site da Microsoft, e que eu
traduzi, para ilustrar o problema, e, depois mostrar como o MEF atua:
(fonte:
http://msdn.microsoft.com/en-us/library/dd460648.aspx
O problema:
Imagine que você é o arquiteto de uma aplicação de grande porte que deve
fornecer suporte para extensibilidade. A sua aplicação tem de incluir um número
potencialmente grande de componentes menores, e é responsável pela sua criação e
execução.
A abordagem mais simples para o problema é incluir os componentes como
código-fonte em seu aplicativo, e chamá-los diretamente em seu código. Isto tem
uma série de inconvenientes óbvios. Além disso, você não vai poder adicionar
novos componentes sem modificar o código-fonte, uma restrição que pode ser
aceitável, por exemplo, em uma aplicação Web, mas é impraticável em um
aplicativo cliente. Outro problema é que você pode não ter acesso ao
código-fonte para os componentes, porque podem ser desenvolvidos por terceiros.
Uma abordagem um pouco mais sofisticada seria fornecer um ponto de extensão
ou de interface, para permitir o desacoplamento entre o aplicativo e seus
componentes. Sob este modelo, você pode fornecer uma interface que um componente
pode implementar, e uma API para que possa interagir com seu aplicativo. Isso
resolve o problema de exigir acesso ao código fonte, mas ainda tem suas próprias
dificuldades.
Como a aplicação não tem qualquer capacidade para descobrir os componentes
por conta própria, você terá que definir explicitamente quais componentes
estão disponíveis e devem ser carregados. Isto é feito através do registro dos
componentes disponíveis em um arquivo de configuração. Isso significa que para
garantir que os componentes estejam corretos criamos um problema de manutenção,
especialmente se você for o usuário final e não o desenvolvedor.
Além disso, os componentes são incapazes de se comunicar uns com os outros,
exceto por meio dos canais rigidamente definidos no próprio aplicativo. Se o
arquiteto da aplicação não antecipou a necessidade de uma comunicação
particular, isso pode torna-se um problema praticamente impossível de resolver.
Finalmente, os desenvolvedores do componente devem aceitar uma forte dependência
ao assembly que contém a interface a implementar. Isso torna difícil para
um componente ser usado em mais de um aplicativo, e também pode criar problemas
quando você cria um framework de teste para componentes.
A proposta do MEF:
Ao invés de ter que realizar o registro dos componentes disponíveis em arquivos de configuração o MEF fornece uma forma de descobri-los implicitamente via composição.
Um componente MEF, chamado part, declarativamente especifica suas dependências (conhecido como importações) e quais capacidades (conhecido como exportações) ele disponibiliza. Quando um componente part é criado, o mecanismo de composição MEF satisfaz suas importações com o que está disponível a partir de outros componentes part.
Em vez deste registro explícito dos componentes disponíveis o MEF fornece uma maneira de descobri-los implicitamente, via composição.
O núcleo do modelo de
composição MEF é o recipiente composition, que contém todas as peças
disponíveis e executa a composição.( a correspondência entre as importações
e as exportações). O tipo mais comum de recipiente composição é o CompositionContainer. |
Quando um part é criado, o mecanismo de composição MEF satisfaz suas
importações com o que está disponível a partir de outros componentes part.
Esta abordagem resolve os problemas discutidos na seção anterior.
Como os componentes part do MEF declarativamente especificam as suas
capacidades, eles são descobertos em tempo de execução, o que significa que um
aplicativo pode fazer uso dos componentes part sem qualquer referência
fixa ou arquivos de configuração.
O MEF permite que os aplicativos descubram e examinam os componentes part
por seus metadados, sem instanciá-los ou até mesmo carregar seus assemblies.
Como resultado, não existe a necessidade de especificar quando e como as
extensões devem ser carregadas.
Além de suas exportações, um part pode especificar as suas
importações, que será preenchido por outros componentes part. Isso faz com que a
comunicação entre os part, seja possível e fácil e permite boa fatoração
do código.
Como o modelo MEF não requer nenhuma dependência fixa de um assembly particular
ele permite que as extensões sejam reutilizadas de aplicação para aplicação.
Isso também torna mais fácil o desenvolvimento de testes, independente da
aplicação, para testar os componentes de extensão.
Uma aplicação extensível escrita através da utilização do MEF declara uma importação que pode ser preenchida por componentes de extensão, e pode também declarar as exportações, a fim de expor serviços do aplicativo para as extensões. Cada componente de extensão declara uma exportação, e pode também declarar importações. Desta forma, os componentes de extensão são automaticamente extensíveis entre si.
Isso simplifica muito a definição de extensibilidade nas aplicações pois podemos resumir a estrutura de atuação do MEF em 3 partes básicas:
Como já foi mencionado essas definições são feitas através do modelo de programação por atributos onde os Importações e as Exportações são feitos através da marcação usando os atributos Import e Export.
Além disso temos que definir um contrato que é uma interface de comunicação que serve de base para a extensão, ou seja, temos que definir o contrato que vai ser seguido, neste contrato definimos o que queremos exportar de funcionalidade e como vamos exportar.
A fim de descobrir os componentes part que estão disponíveis, o
container de composição utiliza um catálogo (Catalog).
Um catálogo é um objeto que torna disponíveis os componentes part que
foram descobertos a partir de alguma fonte. O MEF fornece catálogos para
descobrir os componentes part de um tipo previsto, um assembly ou um
diretório. (Os desenvolvedores podem facilmente criar novos catálogos
para descobrir componentes part de outras fontes, tais como um serviço Web.)
Resumindo:
1 - Uma aplicação declara usando um atributo
Import que depende de uma
implementação que respeite o contrato definido;
2 - Todos os componentes que respeitarem o contrato definido são marcados com o
atributo Export;
Para utilizar o MEF, você precisa de quatro elementos:
- O Contrato é o ponto comum entre um import e um export, e
indica a funcionalidade esperada de uma parte. Em geral é definido através de
uma interface, mas pode ser um tipo abstrato também;
- O Import é onde a parte será plugada, e pode ser uma propriedade, uma
coleção ou construtor de uma classe. É definido através do atributo [Import]
ou [ImportMany]
- O Export é o plugin, a parte que será plugada, e, contém a
implementação da funcionalidade definida pelo contrato. É definido pelo atributo
[Export].
- O Catálogo contém os tipos exportados e que podem ser utilizados
para satisfazer os Imports de cada contrato;
O MEF apresenta quatro tipos de catálogos:
Existem diversos outros tipos de catálogos que podem ser encontrados na internet, inclusive alguns que usam mapeamento XML. Os catálogos são utilizados pelo container no MEF (CompositionContainer) para criação dos tipos.
Abaixo vemos a figura que mostra um esquema simplificado do funcionamento do MEF:(fonte: http://mef.codeplex.com/wikipage?title=Overview&ProjectName=mef)
Colocando a teoria em prática
Vamos agora mostrar como usar os recursos básicos do MEF em uma aplicação bem simples que embora não apresenta um cenário real serve como propósito de apresentação e entendimento do MEF.
Se você ainda não o fez baixe os binários atualizados do MEF em : http://mef.codeplex.com/releases/view/79090
Vamos criar um exemplo bem básico de um aplicativo do tipo Console (baseado em um exemplo de Brad Abrams) usando o Visual C# 2010 Express Edition que imprima a mensagem: "Bem vindo ao MEF".
Abra então o Visual C# 2010 Express Edition e crie um novo projeto do tipo Console Application com o nome Usando_MEF;
A seguir defina o seguinte código no arquivo Program.cs:
using System;namespace Usando_MEF{ class Program { public void Run() { Console.WriteLine("Apresentando MEF"); Console.ReadKey(); } static void Main(string[] args) { Program p = new Program(); p.Run(); } } } |
Vamos agora alterar nosso programa conforme o código abaixo apenas para termos uma motivação para usar o MEF:
using System;namespace Usando_MEF{ class Program {
public
string
Mensagem { get;
set;
} public class Apresentacao { public String Mensagem { get { return "Apresentando MEF"; } } }
{ Apresentacao ap = new Apresentacao();
Mensagem = ap.Mensagem; Console.WriteLine(Mensagem); Console.ReadKey(); } static void Main(string[] args) { Program p = new Program(); p.Run(); } } } |
O código alterado funciona da mesma forma
mas com a alteração introduzimos um forte acoplamento com a classe Apresentação pois temos que criar uma instância desta classe no método Run(); Vamos então criar um ponto de extensão nas linhas: Apresentacao ap = new Apresentacao(); Mensagem = ap.Mensagem;
Vamos fazer isso usando os recursos do MEF.
|
Clique com o botão direito sobre o nome do projeto e selecione Add Reference;
A seguir na janela Add Reference clique na aba Browse e localize os binários onde você instalou os arquivos do MEF:
Você só precisa selecionar a referência a System.ComponentModel.Composition.CodePlex.dll e clicar no botão OK;
A seguir defina os seguintes namespaces no início do arquivo Program.cs:
using
System;Agora vamos alterar o código do método Run() usando os recursos do MEF conforme abaixo:
1 public void Run()2 { 3 //Apresentacao ap = new Apresentacao(); 4 //Mensagem = ap.Mensagem;5 var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); 6 var container = new CompositionContainer(catalog); 7 var batch = new CompositionBatch(); 8 batch.AddPart(this); 9 container.Compose(batch); 11 Console.WriteLine(Mensagem); 12 Console.ReadKey(); 13 }
|
No código acima temos :
- Na linha 5 criamos um catalogo chamado catalog que indica ao MEF onde procurar os imports e exports. Neste caso nós estamos informando que para procurar no assembly atual;
- Na linha 6 criamos um container Composition e dessa forma juntamos
todas as partes;
- Na linha 7 criamos um CompositionBatch que contém todas as partes que
serão adicionadas ou removidas;
- Na linha 8 incluímos uma instância do Programa para o container de forma que
suas dependências sejam vinculadas;
- Na linha 9 efetuamos a composição; é aqui onde a propriedade Mensagem do
programa é obtida;
Executando o projeto iremos obter o mesmo resultado inicial mostrando que o MEF
fez o trabalho direitinho...
Esta é uma pequena introdução ao MEF onde eu não entrei em detalhes de funcionamento nem na sua estrutura; preocupei-me apenas em apresentar o conceito e mostrar como ele funciona de forma bem simples.
Aguarde em breve mais artigos sobre o assunto...
Pegue o projeto completo aqui: Usando_MEF.zip
Col 3:2
Pensai nas coisas que são de cima, e não nas que são da terra;Referências: