Du hast einen neuen Prozess den du in deine monolithische Architektur einbinden musst und sie fällt wie ein Kartenhaus vor deinen Augen zusamen? Du musst nur einen Verarbeitungsschritt hinzufügen und plötzlich fällt deine Anwendung in sich zusammen wie ein Soufflé?
Dann wird es Zeit sich mit Modularisierung und Plugins zu beschäftigen. Ich habe das zum ersten Mal getan und verrate dir meine Erfahrungen damit. Zusätzlich gebe ich dir Tipps die ersten Schritte zu meistern.
Modularisierung was ist das eigentlich?
Du kennst sie bestimmt auch, diese Software bei der alles wie in einem Netz fest verdrahtet ist und alles aufeinander aufbaut. Das nennt sich monolithische Entwicklung. Sie ist weder besonders gut zu warten noch ist sie gut zu erweitern.
Modularisierung im Gegensatz dazu stellt ein Gerüst dar mit dem diese festen Abhängigkeiten aufgelöst werden können. Du kannst dafür verschiedene Ansätze und Methoden wählen, ich stelle dir heute das Managed Extensibility Framework MEF vor.
Was sind Design Patterns?
Grob gesagt sind Design Patterns getestete Lösungsmöglichkeiten für Probleme die schon viele andere vor dir hatten. Nutzt du sie:
- können andere Entwickler deinen Code besser verstehen
- kannst du die Expertise von vielen anderen schlauen Menschen nutzen
- kannst gegenüber deinem Chef mit Fachwissen glänzen
Wenn dich Design Patterns interessieren findest du in den Quellen zwei Links dazu.
Warum sollte mich das interessieren?
MEF bietet dir eine Möglichkeit Abhängigkeiten in deinem Code zu verringern, indem du Design Pattern für die Software Entwicklung wie das Inversion of Control oder das Strategy Pattern verwendest. Damit machst Du deine Anwendungs flexibler und kannst auf Änderungen schnell reagieren.
Und glaub mir, Änderungen sind so ziemlich die einzige Konstante in der Softare Entwicklung.
MEF, was ist das denn nun?
Am besten kann ich dir erklären was MEF ist, indem ich dir ein Beispiel vorstelle:
Stell dir vor du programmierst eine Software in C# bei der verschiedene Operationen mit Dateien durchgeführt werden können. Die Software kann Dateien mit verschlüsseln, komprimieren und in ein Zielformat hüllen. Anfangs denkst du dir noch alles schön, wählst einen Algorithmus für die Verschlüsselung, einen für die Komprimierung und ein Zielformat. Diese drei lässt du dir von deinem Chef genehmigen und implementierst sie.
Doch dann kommt vom Kunden der Wunsch das er noch jeweils eine Alternative braucht. Wieder denkst du dir kein Problem, suchst die Alternativen, programmierst sie und kompilierst die neue Version. Dabei bemerkst du schon das der Kompiliervorgang ewig dauert. Freudig präsentierst du deinem Chef die neue Version aber der Kunde hat schon wieder Änderungswünsche. Es muss unbedingt noch dieser und jener Algorithmus rein, der soll so state-of-the-art sein.
Jetzt kommst du ins grübeln, gibt es nicht eine Möglichkeit nur die sich ändernden Algorithmen zu kapseln und updaten zu müssen. Muss wirklich jedes mal ein komplettes Deployment sein?
Hier kommt MEF ins Spiel. Du verschiebst die Verschlüsselungs-, die Komprimierungs und die Zielformat Klassen in ein neues Projekt und lässt in dem Hauptprojekt nur das Interface welches du mit einem Metatag versiehst. Zusätzlich fügst du ein paar Zeilen Code hinzu.
In einem zweiten Projekt fügst du die Implementierenden Klassen ein. Diese versiehst du jeweils mit einem passenden Metatag und lässt das Projekt als Dll kompilieren. In beide Projekte importierst du dir über NuGet noch MEF und im einfachsten Fall bist du damit fertig.
Welchen Vorteil du dadurch hast wirst du dich fragen. Ganz einfach du hast jetzt eine (oder mehrere) Dll(s) die du:
- separat kompilieren kannst
- separat testen kannst
- separat deployen kannst
Ein Beispiel
Im folgenden zeige ich dir ein Beispiel welches Verpackungsworkflows als Plugins realisiert:
Zuerst erstellen wir ein ClassLibrary Projekt, den Namen darfst du dir aussuchen. Ich habe es Plugin genannt. Dieses Projekt wirst du später in die anderen einbinden.
Dort definierst du ein Interface IWorkflowPlugin wie folgt:
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo(„Plugins“)]
[assembly: InternalsVisibleTo(„Packaging“)]
namespace Packaging.Plugin.Workflow
{
interface IWorkflowPlugin
{
void Setup();
string GetName();
int[] GetInventory();
}
}
Als nächstes legst du ein neues Projekt in einer neuen Solution an. Ich habe es Packaging genannt. Dieser Solution fügst du das Existierende Projekt Plugin hinzu (oder wie du es gennant hast). Anschließend setzt du noch einen Verweis auf das Projekt.
Definiere im Hauptprogramm eine Liste in der die Plugins gehalten werden sollen wie folgt:
using Packaging.Plugin.Workflow;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace Packaging
{
public partial class Form1 : Form
{
[ImportMany(typeof(IWorkflowPlugin))]
private Packaging.Plugin.Workflow.IWorkflowPlugin[] Workflows = null;
Das ImportMany besagt das 0-x Plugins geladen werden sollen. Typeof(IWorkflowPlugin) definiert das Interface das die Plugins erfüllen müssen damit sie geladen werden können.
Der Namespace und der Name der Klasse sind dabei frei wählbar.
Ebenso kannst du im Hauptprogramm mit der folgenden Prozedur alle Plugins aus dem angegebenen Unterverzeichniss Plugins\Workflow laden:
private void LoadWorkflows()
{
var catalog = new DirectoryCatalog(„.\\Plugins\\Workflow\\“);
var container = new CompositionContainer(catalog);
try
{
container.ComposeParts(this);
}
catch (CompositionException e)
{
// „unable to load one or more of the requested types“ hints at a
// ReflectionTypeLoadException, so cast to that
var loadException = e.Errors.First();
// as the error said,
// „Retrieve the LoaderExceptions property for more information“
var cause = loadException.Exception;
// print, log or extract the information in some other way
MessageBox.Show(cause.Message);
}
}
Es gibt verschiedene Kataloge, DirectoryCatalog ist nur einer von Ihnen der als Quelle Dateien verwendet. Der Compositioncontainer kümmert sich um die Erzeugung der Plugin Instanzen. Der Aufruf von ComposeParts ist das Startsignal für Compositioncontainer.
Diese Prozedur musst du natürlich auch aufrufen, ich tue das im Konstruktor der Form Form1.
Jetzt legst du das letzte der drei Projekte an, wieder in einer eigenen Solution. Meine heißt Plugins und deine? Auch hier lädst du das existierende Projekt Plugin und setzt einen Verweis darauf.
Dir fehlt nur noch eine Beispielimplementierung des Interfaces:
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo(„WindowsFormsApplication“)]
namespace Plugins
{
[Export(typeof(Packaging.Plugin.Workflow.IWorkflowPlugin))]
public class KundeA : Packaging.Plugin.Workflow.IWorkflowPlugin
{
private int[] Inventory = new int[4];
public void Setup()
{
}
public string GetName()
{
return „KundeA“;
}
public int[] GetInventory()
{
return Inventory;
}
}
}
[Export] markiert die Klasse zum Export, allerdings vom Typ KundeA. Damit würde sie nicht geladen werden, daher musst du wie schon erwähnt den Typ des Interfaces angeben. Denk daran, diese Implementierung gehört in ein anderes Projekt.
Du benötigst durchgehend die Includedirektiven:
- using System.ComponentModel.Composition;
- using System.ComponentModel.Composition.Hosting;
Auch musst du durchgehend per NuGet MEF installieren.
Damit hast du dein erstes Projekt mit MEF in C# erstellt. Ich gratuliere dir. Im Anhang findest du die drei Projekte und kannst damit experimentieren.
Wie kann es weitergehen
Mit dem folgenden Code kannst du z. B. die Namen der geladenen Plugins einer Combobox cmbWorkflow hinzufügen:
foreach (IWorkflowPlugin workflow in Workflows)
{
cmbWorkflow.Items.Add(workflow.GetName());
}
Probleme und ihre Lösungen
1. Problem: Der Compiler erkennt eine Methode in der Implementierung eines Interfaces nicht (richtig):
Dieser Fehler hat mich einen Tag gekostet und mich halb verrückt gemacht. Ich habe alles mögliche probiert, nichts hat geholfen.
Lösung: Erstellen eines neuen Projektes und übertragen der SourceCode Dateien.
2. Problem: Nach dem installieren von MEF mittels NuGet erhälst du beim ersten Start der Anwendung die Exception FileNotFoundExcetion System.ComponentModel.Composition.CodePlex, Version=4.1.2.0, Culture=neutral, PublicKeyToken=13e5ffd4e05db186.
Die Meldung kommt daher, das beim Installieren gegen die falsche Datei verlinkt wird.
Lösung: Lösche den Verweis System.ComponentModel.Composition.CodePlex und füge einen neuen Verweis hinzu mittels Browse auf packages\VVVV.System.ComponentModel.Composition.Codeplex.2.5.0\lib\net40\System.ComponentModel.Composition.CodePlex.dll.
3. Problem: Du bekommst die Fehlermeldung: The Export ‚ClassLibrary1.KundeA (ContractName=“Packaging.Plugin.Workflow.IWorkflow“)‘ is not assignable to tyoe ‚Packaging.Plugin.Workflow.IWorkflow‘.
Dabei ist Packaging.Plugin.Workflow.IWorkflow austauschbar, wichtig ist das beide Male das selbe angegeben wird. Das kann durch verschiedene Probleme hervor gerufen werden.
Eine Lösung: Überprüfe ob du das Interface in dem Interface Projekt und in dem anderen Projekt eingebunden hast. Falls ja lösche das Interface in dem Implementierenden Projekt.
Fazit
Ich habe dir gezeigt wie einfach du mit MEF Plugins erstellen kannst. Dazu haben wir 3 Projekte in 2 Solutions erzeugt. Diese findest du auch im Download. Von hier an kannst du experimientieren. Besonders möchte ich auf den Blog von Hendrik Lösch verweisen, welcher dir ein 11 teiliges Tutorial zu diesem Thema von Stefan Henneken verlinkt hat. Ich hoffe ich konnte dir helfen einen guten Einstig in MEF zu bekommen wie ich ihn nicht gefunden hatte.
Wie hat dir dieser Eintrag gefallen? Was hast du anders gemacht? Was wünscht du dir an weiteren Einträgen? Hast du Kritik? Schreibe deine Antworten in die Kommentare ich freu mich drauf.
Download MEF
Quellen
Managed Extensibility Framework (MEF)
MEF Tutorial Fundstück sehr ausführliches MEF Tutorial Blog von Hendrik Lösch verweist auf ein 11 Teiliges Tutorial
Teil 1 der Serie und Vorlage für das Beispiel von Stefan Henneken
Introduction to MEF Programming Howto use MEF
Design Patterns als Beispiel anhand von Java bei Galileo Openbook für Java
Vorlage Exception Handling von Wim Coenen
Der Beitrag C# MEF was ist das und wie kann es dir helfen? erschien zuerst auf Frank the DevOp.