En liten bit av MEF
MEF eller Managed Extensibility Framework är ett gemensamt ramverk och en programmeringsmodell för att bygga en applikation som består av separata delar och komponenter som tillsammans skapar en helhet. MEF finns idag som en separat nedladdning på CodePlex, nyss släppt som en “preview 7” men kommer att ingå i .NET Framework 4.0 som en central del av vårt ramverk. Observera att koden i det här exemplet är skrivet med Visual Studio 2010 Beta 1 och .NET Framework 4.0.
Med hjälp av nyckelord som [Import] och [Export] kan beståndsdelarna exportera sin funktionalitet eller importera andra komponenters funktionalitet. Det som används för att binda dessa komponenter tillsammans är en så kallad “container” som i sin tur har en katalog av komponenter med detta beteende.
Här är ett exempel på hur en applikation kan se ut (ett mycket enkelt exempel, men åtminstone en introduktion).
Jag vill bygga en översättningsapplikation som läser in ett ord från användaren och översätter det ordet till andra språk baserat på separata komponenter. Applikationen kommer att bestå av en WPF-applikation med ett enda fönster bestående av en TextBox och en knapp.
Knappen har sedan logiken för att läsa av textrutans text och skriva tillbaka det översatta ordet i densamma.
Det som jag börjar med i det här fallet är att skapa ett interface som beskriver beteendet som jag vill att mina översättningsklasser kommer att implementera, här är förslaget:
public interface IDictionary
{
string Translate(string wordToTranslate);
}
En översättningsklass skulle kunna se ut som följande:
public class French : IDictionary
{
public string Translate(string wordToTranslate)
{
return "Oui";
}
}
Jag ber om ursäkt för den naiva implementationen av översättning till franska, men det är ungefär så långt som min kompetens i det franska språket sträcker sig.
Sedan implementerar jag applikationens logik enligt följande:
public partial class Window1 : Window
{
IDictionary dictionary;
public Window1()
{
InitializeComponent();
dictionary = new French();
}
private void btnTranslate_Click(object sender, RoutedEventArgs e)
{
var word = textBox1.Text;
textBox1.Text += " " + dictionary.Translate(word);
}
}
Nu fungerar applikationen som jag hade tänkt det, men använder fortfarande inte MEF, och är inte heller speciellt flexibel i sin struktur baserat på att jag anar att jag kommer att vilja ha stöd för fler språk i applikationen.
Jag börjar med att se till att min översättningsklass exponerar och exporterar sin funktionalitet enligt följande:
[Export(typeof(IDictionary))]
public class French : IDictionary
{
public string Translate(string wordToTranslate)
{
return "Oui";
}
}
Och i själva fönstret markerar jag instansen “dictionary” enligt följande:
[Import]
IDictionary dictionary;
Det som jag också behöver se till är att ersätta instansieringen av “dictionary” med MEF’s teknik och metod att skapa instanser. Jag ersätter alltså instansieringen i konstruktorn till följande kod:
var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
Det kanske inte är helt självförklarande vad som händer, men i korta drag så skapar jag först en så kallad katalog som i det här fallet undersöker den exekverande applikationen om det finns komponenter som använder [Export] eller [Import]. Den katalogen skickar jag som en parameter till en “container” som används för att koppla samman alla komponenter i katalogen med hjälp av det sista anropet till ComposeParts.
Applikationen fungerar nu på samma sätt som tidigare, skulle det vara så att det inte kompilerar så är det antagligen så att följande namnrymder inte finns tillagda i filen:
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System.ComponentModel.Composition;
För att ytterligare visa på styrkan av MEF så vill jag nu lägga till ytterligare språk till översättningen. Jag skapar därför två ytterligare översättningsklasser på liknande sätt som tidigare:
[Export(typeof(IDictionary))]
public class Spanish : IDictionary
{
public string Translate(string wordToTranslate)
{
return "Si";
}
}
[Export(typeof(IDictionary))]
public class Swedish : IDictionary
{
public string Translate(string wordToTranslate)
{
return "Ja";
}
}
Men hur får jag en variabel som bara är en instans av ett interface till att innehålla flera klasser, precis, det gör jag inte. Istället väljer jag att ersätta den tidigare instansen av “dictionary” till att se ut som följande:
[ImportMany]
IEnumerable<IDictionary> dictionaries;
[ImportMany] atributet säger till MEF att hitta alla komponenter som exporterar interfacet IDictionary och populera samlingen “dictionaries” med dessa. Genom att sedan skapa en enkel for-each-loop runt mitt anrop till Translate så får jag ett önskat beteende:
foreach (var dictionary in dictionaries)
{
textBox1.Text += " " + dictionary.Translate(word);
}
Så kan vi alltså använda MEF i sin grundläggande form för att bygga flexibla och utbyggbara applikationer. Bland annat finns det kataloger för att undersöka en katalog i filsystemet om det finns andra “assemblies” som exporterar och importerar funktionalitet, och det finns till och med MEF för Silverlight som möjliggör asynkron laddning och instansiering av funktionalitet från servern.
Comments
Anonymous
September 03, 2009
The comment has been removedAnonymous
September 04, 2009
Jag är lite fundersam på varför man vill ha detta? Jag kan redan nu göra exakt samma sak med exempelvis StructureMap, men med mindre kod. Dessutom blir jag inte beroende av några attribut, utan jag använder helt enkelt POCO-klasser. Kan skriva ihop ett exempel som gör precis det du har visat i StructureMap och med mindre kod. MEF är enligt mig ännu en onödig komponent från Microsoft. (Ni borde satsa mer energi på Unity istället.)Anonymous
September 04, 2009
Joel: Jag håller själv på att försöka greppa förhållandet mellan Unity <-> MEF <-> PRISM och i och med det naturligtvis också IoC och hur det förhåller sig till ovanstående, när jag har rätat ut mina egna frågetecken så återkommer jag :) Kim: Intressant kommentar, jag skulle gärna vilja se exemplet ovan implementerat med StructureMap. En sak som jag personligen tycker är trevligt med MEF är att om jag byter till exempelvis en DirectoryCatalog istället för AssemblyCatalog så kan jag alltså ladda komponenterna från hårddisken utan omkompliering. Skicka mig gärna ett kodexempel så ska jag publicera det på bloggen också!