Wilfried Woivré & .Net

Unity : Gestion des paramètres primitifs

AVRI28

On a vu précédemment qu’avec de l’IoC comme Unity, il est facilement possible d’instancier des objets avec des paramètres complexes qui ont été au préalable enregistrés dans Unity. Cependant, il est aussi possible de résoudre des objets qui contiennent des types primitifs

Pour un cas concret, prenons ce code qui contient des paramètres optionnels.

public class MyClass : IMyInterface
{
    public MyClass(string param1, int param2, string param3 = "test", int param4 = 42)
    {
        Console.WriteLine("Params : {0}, {1}, {2}, {3}", param1, param2, param3, param4);
    }
}

Si l’on souhaite résoudre via Unity cette classe, il nous faut donc définir les paramètres, pour cela nous allons utiliser la classe ParametersOverride pour passer nos paramètres lors de la résolution de notre classe.

UnityRoot.Container.Resolve<IMyInterface>(new ParameterOverrides()
                                              {
                                                  {"param1", "val"},
                                                  {"param2", 3},
                                                  {"param3", "toto"},
                                                  {"param4", 12}
                                              });

 

Ainsi lors de la résolution de notre container, nous aurons bien la valeur de nos 4 paramètres.

image

 

Alors, comme vous avez pu le voir, dans mon exemple j’ai indiqué des paramètres optionnels, que j’ai néanmoins voulu résoudre lors de l’instanciation de ma classe. Parce qu’en effet à ce jour, Unity ne gère pas les paramètres optionnels, c’est à dire que si je souhaite résoudre ma classe de cette façon :

UnityRoot.Container.Resolve<IMyInterface>(new ParameterOverrides()
                                              {
                                                  {"param1", "val"},
                                                  {"param2", 3}
                                              });

Il se produira une erreur de type Microsoft.Practices.Unity.ResolutionFailedException car celui-ci n’arrive pas à résoudre les paramètres optionnels, comme on peut le voir ci-dessous

image

 

Donc mon conseil, soit vous passez par des propriétés que vous injectez, ce qui vous évite d’avoir des paramètres optionnels que vous devez absolument saisir. Soit vous pouvez définir tous vos paramètres de façon non optionnel.

Remonter

Unity : Enregistrer des types génériques et les résoudre

MARS15

Bon une fois n’est pas coutume, cela fait vraiment longtemps que je n’ai pas publié un billet n’ayant pas de rapport avec la plateforme Windows Azure. Même si j’avoue que je viens d’utiliser cette astuce dans la future version de mon blog sur Azure, mais vous en serez plus très bientôt j’espère !

 

Alors ma problématique est la suivante, je souhaitais faire un cache générique pour mon application, et bien entendu, j’utilise Unity dans tous mes projets, et donc celui ci ne fait pas exception à la règle. J’ai donc cherché à résoudre mes dépendances selon le type dont j’ai besoin.

 

On va donc commencer par créer une interface générique comme celle-ci  :

  1. public interface MyGenericInterface<T>
  2. {
  3.     void DisplayType(T instance);
  4. }

 

On va ensuite l’implémenter dans une classe :

  1. public class MyClass : MyGenericInterface<String>
  2. {
  3.     public void DisplayType(String instance)
  4.     {
  5.         Console.WriteLine(instance.GetType().FullName + Environment.NewLine);
  6.     }
  7. }

 

Voyons maintenant comment l’enregistrer dans un conteneur Unity :

  1. private static void Configure(IUnityContainer container)
  2. {
  3.     container.RegisterType(typeof (MyGenericInterface<>), typeof(MyClass));
  4. }

Comme on peut le voir, on enregistre les types de façon assez classique, il nous faut juste ajout les chevrons afin de définir un paramètre générique.

Si par hasard, vous avez plusieurs paramètres, par exemple, il faudra faire comme ci-dessous afin de définir qu’il y a bien 3 paramètres génériques

  1. private static void Configure(IUnityContainer container)
  2. {
  3.     container.RegisterType(typeof (MyGenericInterface<,,>), typeof(MyClass));
  4. }

 

Et maintenant, voyons comment résoudre nos types, si on prend l’exemple suivant :

  1. UnityRoot.EnsureInitialized();
  2. UnityRoot.Container.Resolve<MyGenericInterface<String>>().DisplayType("Hello");
  3.  
  4. try
  5. {
  6.     UnityRoot.Container.Resolve<MyGenericInterface<Int32>>().DisplayType(42);
  7. }
  8. catch (Exception ex)
  9. {
  10.     Console.ForegroundColor = ConsoleColor.Red;
  11.     Console.WriteLine(ex);
  12.     Console.ForegroundColor = ConsoleColor.White;
  13. }

 

On va donc essayer de résoudre une classe implémentant notre interface avec le type String, et ensuite avec le type Int32. Dans notre cas, nous n’avons ajouté qu’une classe avec une interface avec le type String.

Si nous lançons notre application, nous allons donc avoir ceci  :

image

 

Nous avons bien la résolution de notre premier élément, par contre la deuxième ne marche pas comme prévu, puisque nous n’avons pas enregistrer de classe avec notre interface implémentant le type Int32.

Voilà, je ne vous donne pas le code source de la solution, puisque tout est là, il ne manque que les références à Unity, et pour cela NuGet est votre meilleur ami !

Remonter

ASP.Net & Unity : Gestion des contrôleurs

JANV3

Dans un article précédent, je vous ai montré comment injecter vos différentes dépendances via Unity en utilisant plusieurs techniques. Si depuis, vous avez voulu essayer en ASP.Net MVC, vous vous êtes surement aperçu que l'on ne peut pas directement résoudre nos dépendances puisque la création des différents contrôleurs est par défaut géré par le Framework.

Donc avant de voir comment modifier cela, il faut comprendre comment une page est construite. (Image issue de : http://msdn.microsoft.com/en-us/magazine/dd695917.aspx)

image

 

On peut donc voir que lors du Worflow de création d’une page ASP.Net MVC, le MvcHandler interagit avec le IControllerFactory qui va lui fournir le, ou les, instances de IController qui correspondent, soient nos contrôleurs que nous utilisons en ASP.Net MVC. Et bien entendu, vous pouvez redéfinir vous même votre propre fabrique afin de pouvoir y placer la création de vos contrôleurs via Unity.

 

Déjà commençons par créer notre classe qui enregistre notre Container Unity :

public class UnityRoot
{
    private readonly static IUnityContainer _container = new UnityContainer();

    internal  static void EnsureInitialized()
    {
        
    }

    static UnityRoot()
    {
        Configure(_container);
        var controllerFactory = new UnityControllerFactory(_container);
        ControllerBuilder.Current.SetControllerFactory(controllerFactory);
    }

    private static void Configure(IUnityContainer container)
    {
        container.RegisterType<Interface1, ClassImpl1>();
        container.RegisterType<Interface2, ClassImpl2>();
    }
}

 

Ensuite, comme on peut le voir aux lignes 13 et 14, je créé une instance de IUnityControllerFactory qui va gérer la création des différents contrôleurs.

public class UnityControllerFactory : DefaultControllerFactory
{
    private IUnityContainer container;

    public UnityControllerFactory(IUnityContainer container)
    {
        this.container = container;
    }

    protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
    {
        IController controller;
        if (controllerType == null)
            throw new HttpException(404, String.Format("The controller for path '{0}' could not be found" +
                "or it does not implement IController.", requestContext.HttpContext.Request.Path));

        if (!typeof(IController).IsAssignableFrom(controllerType))
            throw new ArgumentException(string.Format("Type requested is not a controller: {0}", controllerType.Name), "controllerType");
        try
        {
            controller = container.Resolve(controllerType) as IController;
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException(String.Format("Error resolving controller {0}", controllerType.Name), ex);
        }
        return controller;
    }
}

Et voilà, uniquement en surchargeant la méthode GetControllerInstance, vous pouvez modifier la création de vos contrôleurs, ici en ajoutant les différentes implémentations de Interface1 et Interface2.

 

Voici les sources de l’application avec laquelle j’ai fait cet article.

 

Et merci à Julien Corioland de m’avoir donné cette astuce.

Remonter

Unity : Comment injecter ces données ?

DÉCE2

Il y a quelques temps, dans un article, j’ai présenté l’injection de dépendances via Unity sous C# 3.5, vous êtes bien entendu au courant, que cette technologie n’est pas morte avec C# 4.0.

Il existe donc deux méthodes, on peut soit injecter ces différentes instances via le constructeur de notre classe, ou alors en injectant les différentes propriétés de la classe. Donc si vous débutez avec unity, on peut se demander comment l’on fait cette opération, je vais donc vous présenter ces deux méthodes ci dessous, soit via notre code dans notre application, ou via la configuration XML d’Unity.

 

L’injection via le constructeur, est celle que j’avais utilisé dans une précédente démonstration, on va donc prendre un cas très simple pour montrer comment injecter des données via le constructeur.

Commençons par la gestion d’Unity via le fichier de configuration :

  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <alias alias="Interface1" type="Demo.UnityConstructor.Interface1, Demo.UnityConstructor" />
    <alias alias="Interface2" type="Demo.UnityConstructor.Interface2, Demo.UnityConstructor" />
    <alias alias="ITest" type="Demo.UnityConstructor.ITest, Demo.UnityConstructor" />
    <alias alias="ClassImpl1" type="Demo.UnityConstructor.ClassImpl1, Demo.UnityConstructor" />
    <alias alias="ClassImpl2" type="Demo.UnityConstructor.ClassImpl2, Demo.UnityConstructor" />
    <alias alias="TestConstructor" type="Demo.UnityConstructor.TestConstructor, Demo.UnityConstructor" />


    <container>
      <register type="Interface1" mapTo="ClassImpl1" name="classImpl1" />
      <register type="Interface2" mapTo="ClassImpl2" name="classImpl2" />
      <register type="ITest" mapTo="TestConstructor" name="FULL">
        <constructor>
          <param name="interface1" type="Interface1">
            <dependency name="classImpl1" type="ClassImpl1" />
          </param>
          <param name="interface2" type="Interface2">
            <dependency name="classImpl2" type="ClassImpl2" />
          </param>
        </constructor>
      </register>
      <register type="ITest" mapTo="TestConstructor" name="INTERFACE1">
        <constructor>
          <param name="interface1" type="Interface1">
            <dependency name="classImpl1" type="ClassImpl1" />
          </param>
        </constructor>
      </register>
      <register type="ITest" mapTo="TestConstructor" name="INTERFACE2">
        <constructor>
          <param name="interface2" type="Interface2">
            <dependency name="classImpl2" type="ClassImpl2" />
          </param>
        </constructor>
      </register>
      <register type="ITest" mapTo="TestConstructor" name="EMPTY">
        <constructor />
      </register>
    </container>
  </unity>

Donc on peut voir rapidement via ce fichier de configuration, qu’on a 3 interfaces (Interface1, Interface2, ITest) ainsi que les 3 classes qui les implémentent (ClassImpl1, ClassImpl2, TestContructor), de même on peut apercevoir que j’enregistre 4 différentes instances de mon TestConstructor avec différents paramètres dans les constructeurs, puisqu’en effet, ma classe TestConstructor contient différent constructeur comme on peut le voir :

public class TestConstructor : ITest
{
    public TestConstructor()
    {
        Console.WriteLine("Constructeur vide !");
    }

    public TestConstructor(Interface1 interface1)
    {
        Console.WriteLine("Constructeur 1 paramètre : Interface 1");
    }

    public TestConstructor(Interface2 interface2)
    {
        Console.WriteLine("Constructeur 1 paramètre : Interface 2");
    }

    public TestConstructor(Interface1 interface1, Interface2 interface2)
    {
        Console.WriteLine("Constructeur 2 paramètres : Interface 1 & 2");
    }
}

Bon maintenant c’est bien beau, mais comment je récupère ma configuration et l’exploite ! Et bien Unity, c’est simple à utiliser, avec simplement 2 lignes de code vous pouvez récupérer votre configuration

private void ConfigureContainerXml()
{
    var configurationSection = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
    configurationSection.Configure(_containerXml);
}

 

Et pour exploiter nos différentes instances, pareil toujours aussi simple

_containerXml.Resolve<ITest>("FULL");
Console.WriteLine("**************");
_containerXml.Resolve<ITest>("INTERFACE1");
Console.WriteLine("**************");
_containerXml.Resolve<ITest>("INTERFACE2");
Console.WriteLine("**************");
_containerXml.Resolve<ITest>("EMPTY");

On remarquera qu’ici, je suis obligé de passer via un nom pour récupérer mes différentes instances de ITest, ceci est dû au fait, que j’en enregistre plusieurs, mais si j’en enregistre qu’une seule, je peux utiliser un unityContainer.Resolve<MonInterface>(); pour résoudre mon injection.

Bon maintenant qu’on a vu comment le faire via un fichier de config, on va voir comment déclarer nos dépendances via le code, et là pareil rien de bien complexe à mon avis :

private void ConfigureContainer()
{
    _container.RegisterType<Interface1, ClassImpl1>();
    _container.RegisterType<Interface2, ClassImpl2>();
    _container.RegisterType<ITest, TestConstructor>("FULL");
    _container.RegisterType<ITest, TestConstructor>("INTERFACE1", new InjectionConstructor(_container.Resolve<Interface1>()));
    _container.RegisterType<ITest, TestConstructor>("INTERFACE2", new InjectionConstructor(_container.Resolve<Interface2>()));
    _container.RegisterType<ITest, TestConstructor>("EMPTY", new InjectionConstructor());
}

Donc dans ce code, ce que je fais c’est enregistrer mes différentes interfaces avec un simple RegisterType, où je peux nommer mon instance. A noter que je passe par un InjectionConstructor pour fournir mes différents paramètres.

Il faut savoir que par défaut, si l’on ne fournit pas d’information pour le constructeur Unity va résoudre le constructeur qui a le plus de paramètre, c’est donc pour cela que j’utilise un InjectionConstructor sans aucun paramètre.

 

Bon maintenant qu’on a fait avec le constructeur, on se dit “oui, c’est bien, c’est beau, mais on peut le faire avec les propriétés ? “, et bien entendu Unity peut aussi le faire ! Et bien entendu, c’est aussi simple qu’avec l’injection de constructeur.

Donc pour commencer, l’injection via le fichier de configuration :

  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <alias alias="Interface1" type="Demo.UnityInjectionProperty.Interface1, Demo.UnityInjectionProperty" />
    <alias alias="Interface2" type="Demo.UnityInjectionProperty.Interface2, Demo.UnityInjectionProperty" />
    <alias alias="ITest" type="Demo.UnityInjectionProperty.ITest, Demo.UnityInjectionProperty" />
    <alias alias="ClassImpl1" type="Demo.UnityInjectionProperty.ClassImpl1, Demo.UnityInjectionProperty" />
    <alias alias="ClassImpl2" type="Demo.UnityInjectionProperty.ClassImpl2, Demo.UnityInjectionProperty" />
    <alias alias="TestInjection" type="Demo.UnityInjectionProperty.TestInjection, Demo.UnityInjectionProperty" />

    <container>
      <register type="Interface1" mapTo="ClassImpl1" name="classImpl1" />
      <register type="Interface2" mapTo="ClassImpl2" name="classImpl2" />
      <register type="ITest" mapTo="TestInjection" name="FULL">
        <property name="Interface1">
          <dependency name="classImpl1" type="ClassImpl1" />
        </property>
        <property name="Interface2">
          <dependency name="classImpl2" type="ClassImpl2" />
        </property>
      </register>
      <register type="ITest" mapTo="TestInjection" name="INTERFACE1">
        <property name="Interface1">
          <dependency name="classImpl1" type="ClassImpl1" />
        </property>
      </register>
      <register type="ITest" mapTo="TestInjection" name="INTERFACE2">
        <property name="Interface2">
          <dependency name="classImpl2" type="ClassImpl2" />
        </property>
      </register>
      <register type="ITest" mapTo="TestInjection" name="EMPTY">
      </register>
    </container>
  </unity>

Bon comme on peut voir, à part les différents paramètres, rien ne change par rapport à la version précédent, il suffit de déclarer les différentes propriétés à instancier ! Et c’est toujours aussi simple ! Bien entendu le reste du code (Construction du container et résolution de l’instance) sont les mêmes !

Maintenant comment enregistrer nos différentes instances via le code :

_container.RegisterType<Interface1, ClassImpl1>();
_container.RegisterType<Interface2, ClassImpl2>();
_container.RegisterType<ITest, TestInjection>("FULL",
    new InjectionProperty("Interface1", _container.Resolve<Interface1>()),
    new InjectionProperty("Interface2", _container.Resolve<Interface2>()));
_container.RegisterType<ITest, TestInjection>("INTERFACE1",
    new InjectionProperty("Interface1", _container.Resolve<Interface1>()));
_container.RegisterType<ITest, TestInjection>("INTERFACE2",
    new InjectionProperty("Interface2", _container.Resolve<Interface2>()));
_container.RegisterType<ITest, TestInjection>("EMPTY");

 

Donc ici, on utilise un InjectionProperty, cependant, c’est légèrement plus verbeux que la version avec les constructeurs, mais bon ce n’est pas encore méchant ! Seul différence, c’est que là pour enregistrer notre classe ayant le plus de propriété il faudra toutes les saisir !

 

Voilà le code source si vous voulez tester la solution :

image

 

Bien entendu, vous pouvez aussi mixer les deux méthodes, si vous avez envie ! Pour ma part, j’utilise principalement la méthode via le constructeur ainsi que via le code, tout simplement parce que je préfère résoudre mes éléments automatiquement et ne pas avoir à déclarer mes différentes propriétés ! De plus via le code, puisque le problème du fichier de configuration, c’est que l’on ne sait pas quand les données de ce fichier ne sont pas correct, enfin du moins avant d’avoir lancer l’application …. Et de plus, il n’y a aucune chance qu’un IT réseau peu compétent fasse du zèle en essayant de modifier seul le fichier de config.

Remonter

Allier MEF et Unity

MARS12

On a vu dans les deux derniers articles comment débuter avec MEF (Managed Extensibility Framework) et Unity, on peut donc maintenant imaginer les possibilités que ceux-ci offrent en terme de développement. Mais il faut avouer que coupler les deux serait encore un plus pour nos architectures. Et bien c’est possible grâce à MEFContrib (http://mefcontrib.codeplex.com, oui Codeplex est une source de framework presque intarissable)

Bon alors imaginons un cas assez concret, comme une application client lourd, celle-ci permet la création (et donc l’ajout) de plugins. Il est bien évident que chacun des plugins aura une tâche particulière. Par exemple, un plugin pourrait afficher des flux RSS, un autre attaquer la base de données de l’application afin d’afficher les différents utilisateurs présent en base. Bref beaucoup de possibilité, on voit donc ici l’intérêt de MEF afin de créer des plugins ayant un traitement spécifique et cependant se liant parfaitement à notre application.

Donc assez de parole, si l’ont crée cette application (tout le code sera fourni à la fin de cet article)

On va donc commencer par créer notre application en utilisant une architecture à base de plugins, voici donc ci-dessous les références entre nos différentes assemblys :

image

Si on détaillait un peu ce qu’il y a dans chaque assembly :

  • Client : Notre application WPF qui hébergera les différents plugins
  • Service : Cette assembly va dans notre cas servir de Repository, elle jouera donc le rôle de base de données.
  • Model : Contient les différentes entités de notre application
  • Contracts : Contient les différentes interfaces nécessaires à notre application
  • Plugin : Contient les différents plugins de notre application

Bon maintenant, qu’on a vu les références dans notre application, si on voyait notre application avec son joli design (ou pas)

image

Je ne vais détaillé tous le code, mais on va tout de même voir les interactions pour MEF. Commençons par notre assembly Contract, elle contient deux interfaces qui sont IModule, et IModuleMetadata, qui sont respectivement, l’interface pour définir un module, et l’interface qui contiendra les metadata de chaque objet.

public interface IModule
{
}
public interface IModuleMetadata
{
string Title { get; }
string Author { get; }
}

Notre première interface va en fait être utilisé pour nos différentes interactions utilisées par MEF, ici, cette interface ne défini aucune méthode puisque nos modules sont indépendants.

Notre deuxième définit un titre un nom d’auteur pour chacun des composants, comme ça on sait sur qui il faut taper quand ça ne marche pas ….

Passons à un module, celui des flux RSS par exemple,

[Export(typeof(IModule))]
[ExportMetadata("Title", "Flux RSS de Wilfried Woivré")]
[ExportMetadata("Author", "Wilfried Woivré")]
public partial class RSSControl : UserControl, INotifyPropertyChanged, IModule

On voit donc ici un export de type IModule, et les différentes metadatas de ce module. Bref, rien de bien compliqué dans notre cas du coup.

Il ne manque plus que notre client WPF qui héberge notre application

public partial class Window1 : Window
{
[ImportMany]
Lazy<IModule, IModuleMetadata>[] modules;

[Export(typeof(Func<List<Person>>))]
private List<Person> GetPersons()
{
return new ServiceMock().GetPerson();
}

On a donc ici notre liste de modules que l’on va chargé grâce à MEF. Dans notre fonction que l’on déclare en mode export, on expose la liste des personnes comprises dans notre application, puisque le module qui affiche ces informations n’a pas accès à l’assembly qui gère cela.

Ici, au terme de cette première étape, on a donc une application modulaire grâce à MEF ! Mais chacun de ces modules est “libre” de faire ce qu’il veut, prenons un cas concret, imaginons que l’on veuille instaurer des logs dans notre application. Comment fait-on ? On a le choix me direz-vous, chacun des modules peut choisir de faire ce qu’il veut, ou alors on essaye d’unifier nos différents logs.

Même autre cas, notre méthode ‘GetPersons()’ qui est exportées par MEF n’est déclarée dans aucune interface, donc il faut rédiger une documentation sur toutes les méthodes disponibles, documentation qui ne sera d’ailleurs peut être même pas lue …

Une solution serait donc de passer via une Factory, en effet, si l’on indique que notre ‘ServiceMock’ implémente une interface IContract, on peut utiliser une seule méthode à exportée qui renverrait une instance de notre ServiceMock, on pourrait donc avoir une seule méthode à exporter qui serait de ce type :

[Export(typeof(Func<IFactory>))]
private IFactory GetPersons()
{
return ServiceMock.GetSingleton();
}

Ainsi, il n’y aurait qu’une fonction principale à répertorier dans notre documentation, pour le reste, l’IntelliSense peut vous aider très facilement, mais bien entendu cela n’empêche pas de faire tout plein de doc !

Bon, vous allez me dire, qu’on arrive enfin à un rendu pas trop mal, mais qu’en est-il d’Unity là dedans …. Grâce à lui nous allons ajouter notre logger dans notre cas.

Alors on sait que MEF est fait pour intégrer des éléments non connus dans notre application, alors qu’Unity travaille sur des éléments connus à l’avance, donc ça peut poser problème au final. Donc heureusement, Codeplex est riche en utilitaire et framework en tout genre, il y a donc comme je l’ai dit le projet MefContrib qui est une bonne base pour créer une application intégrant MEF et Unity.

Pour plus de facilité, j’ai redéfini le Bootstrapper d’Unity afin d’y injecter mes données relatives à MEF.

public class BootStrapper : UnityBootstrapper
{
public CompositionContainer MefContainer { get; private set; }

protected override IUnityContainer CreateContainer()
{
var aggregateCatalog = new AggregateCatalog(new ComposablePartCatalog[] { new DirectoryCatalog(@".\Extensions") });
var unityContainer = new UnityContainer();
MefContainer = unityContainer.RegisterFallbackCatalog(aggregateCatalog);

return unityContainer;
}

protected override void ConfigureContainer()
{
Container.RegisterInstance(MefContainer);
Container.RegisterInstance<ILogger>(new Logger());

base.ConfigureContainer();
}

protected override IModuleCatalog GetModuleCatalog()
{
return new DirectoryModuleCatalog() { ModulePath = @".\Extensions" };
}

protected override System.Windows.DependencyObject CreateShell()
{
var view = Container.Resolve<Window1>();
view.Show();

var mefModules = MefContainer.GetExports<IModule>();
view.LoadModules(mefModules);

return view;
}
}

Ici, on hérite donc de la classe Bootstrapper qui est présente dans Unity, c’est une méthode courante que l’on retrouve souvent lorsqu’on utilise PRISM.

Notre première méthode va donc créer notre container Unity, et va y rajouter un catalogue pointant sur notre dossier de plugin. La deuxième va nous permettre de configurer ce précédent container, et d’y ajouter une instance de type ILogger. La suivante indique le chemin des Module, dans laquelle on va injecter du Unity. Et enfin la dernière va créer notre Shell, et lui affecter les différentes instances de modules.

J’ai de plus redéfini quelques méthodes d’extension pour IUnityContainer que vous pourrez voir dans la source du code, je vous laisse un peu de surprise.

A partir de là, on est presque près, cependant au lancement de l’application on n’a pas les logs actifs, ce qui est un soucis, étant donné que c’est ce qu’on désirait. Afin d’arranger cela, rien de plus simple, il suffit de jouer avec les attributs de MEF en exportant le constructeur prenant une instance de ILogger en entrée.

[ImportingConstructor]
public RSSControl(ILogger logger) : this()
{
Logger = logger;
}

Et là rien de plus, on a bien nos différents logs actifs. On a donc réussi à intégrer MEF avec Unity, grâce à tout plein de librairies…

Bref, pour conclure avant de vous fournir le code source de l’application, l’intégration de MEF avec Unity est un bon moyen de coupler IoC et Extensions, cependant cela ralentit fortement le chargement des applications, et je pense que la maintenance de celles-ci ne doit pas être améliorée étant donné qu’il faut connaitre les deux technologies pour maintenir le Shell. Mais cependant c’est tout de même un beau challenge à réaliser … La suite plus tard, étant donné que mon projet pour la fin de l’année se base sur ces trois derniers articles.

image

Remonter