MEF (Managed Extensibility Framework) est une bibliothèque .NET qui facilite la création d'applications modulaires et extensibles. Elle permet de découpler les composants en utilisant des contrats d'interface, évitant ainsi les dépendances rigides et favorisant la réutilisation des extansions au sein d'une même application ou entre différentes applications.
Pour illustrer son utilisation, suivons un exemple pratique où nous définissons un contrat commun, implémentons plusieurs fournisseurs, et chargeons dynamiquement ces implémentations.
Étape 1 : Définir le contrat d'interface
Créez une bibliothèque de classes nommée ServiceContracts et ajoutez une interface IBankAccount qui spécifie les fonctionnalités de base pour un compte bancaire.
using System;
namespace ServiceContracts { public interface IBankAccount { double Balance { get; set; } string GetAccountDetails(); void Deposit(double amount); void Withdraw(double amount); } }
</div>### Étape 2 : Implémenter le contrat dans une bibliothèque séparée
Créez une autre bibliothèque de classes appelée `BankAlpha`, ajoutez une référence à `ServiceContracts`, puis implémentez l'interface avec l'attribut `[Export]`.
<div>```
using System;
using System.ComponentModel.Composition;
using ServiceContracts;
namespace BankAlpha
{
[Export(typeof(IBankAccount))]
public class AlphaAccount : IBankAccount
{
public double Balance { get; set; }
public string GetAccountDetails()
{
return "Compte AlphaBank";
}
public void Deposit(double amount)
{
Balance += amount;
}
public void Withdraw(double amount)
{
Balance -= amount;
}
}
}
Démarrez un projet console nommé MEFHost, ajoutez une classe principale qui utilise [ImportMany] pour collecter toutes les implémentations de IBankAccount depuis un répertoire spécifique.
using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using ServiceContracts;
namespace MEFHost { class MainApp { [ImportMany(typeof(IBankAccount))] public IEnumerable Accounts { get; set; }
static void Main(string[] args)
{
var app = new MainApp();
app.LoadExtensions();
foreach (var acc in app.Accounts)
{
Console.WriteLine(acc.GetAccountDetails());
}
Console.ReadLine();
}
private void LoadExtensions()
{
var catalog = new DirectoryCatalog("Plugins");
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
}
}
</div>Pour tester, compilez la bibliothèque `BankAlpha` et copiez le fichier DLL résultant dans le dossier `Plugins` (sous le répertoire de sortie de `MEFHost`). L'exécution affichera alors les détails du compte AlphaBank.
### Étape 4 : Ajouter une nouvelle extension sans modifier l'application hôte
Dans la même solution, créez une bibliothèque supplémentaire `BankBeta` avec une classe `BetaAccount` qui implémente `IBankAccount`.
<div>```
using System;
using System.ComponentModel.Composition;
using ServiceContracts;
namespace BankBeta
{
[Export(typeof(IBankAccount))]
public class BetaAccount : IBankAccount
{
public double Balance { get; set; }
public string GetAccountDetails()
{
return "Compte BetaBank";
}
public void Deposit(double amount)
{
Balance += amount;
}
public void Withdraw(double amount)
{
Balance -= amount;
}
}
}
Les avantages de cette approche incluent une meilleure modularité, une réutilisation facilitée des composants entre applications, et l'élimination de la nécessité de recompiler l'ensemble du projet lors de l'ajout de nouvelles fonctionnalités. Cela illustre également l'inejction de dépendances, où les implémentations sont fournies dynamiquement à l'exécution.