Nouvelles Chroniques d'Amethyste

Penser au Sens, pas au Verbe

Injection de dépendance avec Autofac

Poster un commentaire

Les Framework d’injection de dépendance sous .Net sont légions, il est hors de question de les connaître tous bien entendu.

J’ai déjà écris un tuto sur Unity, je recommence avec un autre Autofac. C’est un outil que j’ai découvert récemment et que j’aime vraiment beaucoup. Il est confortable, très agréable à utiliser et plutôt rapide.

Si vous même ne connaissez pas le produit, venez donc le découvrir avec moi.

Vous trouverez également en bibliographie une série de tests de performances.

Présentation d’usage

La page d’accueil (très soignée) se trouve ici:

http://autofac.org/

L’outil prend en charge l’injection de constructeur et l’injection de propriétés. Comme Unity on peut aussi exploiter un réglage par le code ou bien par un fichier de configuration.
Avec ça on doit pouvoir gérer quasiment n’importe quel scénario. Reste à voir avec quelle facilité bien entendu!

Côté style de programmation, Autofac privilégie le fluent interface qui est un modèle à la mode depuis quelques années. C’est un Framework récent, agréable à utiliser, rempli de choses innovantes. Bref je dois dire que j’aime beaucoup.

La doc est également exemplaire et très complète ce qui est rare pour ce genre d’outil.

 

Important également, les Framework .Net supportés:

  • .Net 4.5
  • Silverlight 5
  • Applis Windows Store
  • Appli WinPhone

On peut travailler quoi!

Dernier point, Autofac est disponible via Nuget ce qui est aujourd’hui indispensable.

 

Le mieux est maintenant de faire péter le code avec devinez quoi!

Hello world

On commence avec l’interface très simple suivante:


interface IService
{
   void AfficherMessage(string message);
}

Un exemple typique d’utilisation d’Autofac ressemble à ceci:

private static IContainer _container { get; set; }
 
static void Main(string[] args)
{
   ContainerBuilder containerBuilder = new ContainerBuilder();
   containerBuilder.RegisterType<MonService>().As<IService>();
 
   _container = containerBuilder.Build();
 
   using (ILifetimeScope scope = _container.BeginLifetimeScope())
   {
      IService service = scope.Resolve<IService>();
   }
}

Les choses sont assez simples. On commence par créer une instance de ContainerBuilder que nous allons utiliser pour construire un conteneur comme dans tous les Framework de ce type.

ContainerBuilder expose différentes méthodes en RegisterXXX qui permettent de déclarer un type. Le type peut être fournit de diverses façons:

  • Par réflexion comme dans l’exemple précédent
  • Par réflexion dans un assemblage
  • En fournissant directement l’instance
  • Avec une méthode lambda (méthode anonyme) qui retourne une instance.

Cette dernière méthode est pratique pour le cas des classes que l’on n’instancie pas avec un simple constructeur.

 

Le rattachement à une interface s’effectue ensuite à l’aide de la méthode As<>().

On peut se demander l’intérêt de la méthode As() plutôt que la solution retenue dans Unity. La raison est que l’on dispose ainsi d’un moyen très simple de gérer le cas d’une classe qui expose plusieurs services comme par exemple:


class MonService : IService, IAutreService

On peut alors avoir:


containerBuilder.RegisterType<MonService>()
   .As<IService>()
   .As<IAutreService>();

Et lors de l’exécution on choisit tel ou tel service:


IService service = scope.Resolve<IService>();
IAutreService autreService = scope.Resolve<IAutreService>();

C’est simple et élégant.

 

La gestion du cycle de vie dans Autofac est très différente de celle de Unity et me semble t’il plus clair. Autofac introduit un mécanisme de portée qui est similaire à celui rencontré dans la discussion sur le cas des Web API dans le tuto Unity.

La portée est simplement la zone du code dans laquelle un service peut être partagé avec les composants qui le consomme.

 

L’exemple qui précède montre l’utilisation de BeginLifetimeScope(). Cette fonction déclare une nouvelle portée rattachée à la portée racine qui est elle disponible durant tout le fonctionnement de l’application. C’est la méthode recommandée dans Autofac, on réserve la portée racine pour les singletons.

Les instances définies dans une portée seront automatiquement libérés de la mémoire et disposés s’ils implémentent IDisposable.

Bien entendu il est parfaitement possible d’imbriquer les portées si nécessaire.

Tout comme avec Unity on peut gérer finement la portée. On n’a pas que le choix entre une portée globale et une portée locale. On peut préciser un modèle de nettoyage autre que celui par défaut avec diverses méthodes dont:

  • InstancePerDependency
    Modèle par défaut.
  • InstancePerLifetimeScope
    Une instance valable sur toute la durée de vie de la portée
  • InstancePerMatchingLifetimeScope
  • InstancePerRequest
    Une instance valable sur la durée de la requête

 

Utilisation d’une fonction anonyme

C’est je crois un des points forts d’Autofac et ouvre la porte à divers scénarios intéressants. Voici un code de référence:


containerBuilder.Register(c =>
{
   return new MonService();
}).As<IService>();

On fournit une méthode lambda qui retourne une instance de IService. L’exemple donné est purement formel et n’est pas très intéressant bien entendu.

Mais il peut le devenir si on imagine que la classe MonService ne soit pas instanciée directement par un constructeur ou bien si une méthode d’initialisation a besoin d’être appelée.

Intéressons nous aux paramètres de la fonction lambda. J’ai bien dit ‘aux’ car il peut y en avoir deux en fait.

Le premier, ‘c’, est de type IComponentContext. Ce paramètre est important car il fournit un moyen de résoudre les dépendances que pourrait avoir MonService, par exemple ainsi:


containerBuilder.Register(c =>
{
   return new MonService(c.Resolve<IAutreService>());
}).As<IService>();

Il est recommandé de procéder de la sorte plutôt qu’instancier directement IAutreService afin de bénéficier de l’appel à Dispose déterministe.

Parmi les dépendances se trouvent aussi les paramètres du type. Imaginons que MonService dispose d’une propriété IAutreService:


containerBuilder.Register(c =>
{
   return new MonService() { AutreService = c.ResolveOptional<IAutreService>() };
}).As<IService>();

ResolveOptional effectue la résolution de type, mais ne lève pas d’exception si le type n’est pas enregistré.

 

Un deuxième paramètre est possible. Il s’agit d’un IEnumerable<Autofac.Core.Parameter>. Cette classe du Framework Autofac est utilisée pour passer des paramètres à une instance que l’on tente de créer dans le conteneur.

On pourrait imaginer avoir deux implémentations de IService, disons Monservice et MonAutreService, mais c’est au moment de l’exécution que l’on souhaite en sélectionner une.

containerBuilder.Register<IService>((c, p) =>
{
   int serviceId = p.Named<int>("serviceId");
   if (serviceId == 0)
   {
      return new MonService();
   }
 
   return new MonAutreService();
});

On pourrait ensuite lancer le code suivant:


IService service = scope.Resolve<IService>(new NamedParameter("serviceId", 1));

 

Important: si vous déclarez comme Int la valeur du paramètre, il faut bien passer un Int dans NamedParameter et pas par exemple « 1 ».

On passe donc un paramètre nommé (serviceId). Ce paramètre est analysé au moment de la résolution du type et selon la valeur obtenue, un type plutôt qu’un autre sera instancié.

C’est un scénario que l’on peut gérer avec Unity, mais pas de façon aussi élégante.

Autofac supporte plusieurs types de paramètres:

  • NamedParameter
    Paramètre nommé
  • TypedParameter
    Correspondance par type
  • ResolvedParameter
    Un paramètre dont on peut retrouver la valeur dynamiquement contrairement aux deux autres qui ne peuvent passer que des valeurs constantes

Une utilisation importante des paramètres est bien sûr le passage de valeurs au constructeur. On peut évidemment se servir d’une expression lambda, mais on dispose aussi de la méthode WithParameter.


containerBuilder.RegisterType<MonService>()
   .As<IService>()
   .WithParameter("NomParametre", "Valeur");

 

Bibliographie

 

 

 

 

 

Publicités

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s