2013-08-21 3 views
15

Je suis habitué à créer mes propres usines comme indiqué (ce qui est simplifié pour illustration):usine AutoFac

public class ElementFactory 
{ 
    public IElement Create(IHtml dom) 
    { 
     switch (dom.ElementType) 
     { 
      case "table": 
       return new TableElement(dom); 
      case "div": 
       return new DivElement(dom); 
      case "span": 
       return new SpanElement(dom); 
     } 
     return new PassthroughElement(dom); 
    } 
} 

Je suis enfin décidé d'utiliser un conteneur IoC (AutoFac) dans mon courant projet, et je me demande s'il existe une façon magique de réaliser la même chose avec élégance avec AutoFac?

+1

Quel est l'avantage de remplacer cette usine par Autofac? – Steven

+0

Désolé, comme je l'ai fait allusion dans mon commentaire sur la réponse de KeithS, j'espérais qu'il y aurait un peu de vaudou AutoFac qui m'a permis d'éviter le changement. Quel est l'avantage d'éviter l'interrupteur? Je ne sais pas ... Je semble juste avoir été endoctriné avec une aversion pour eux :( –

+1

IoC n'est pas une baguette magique.Vous pouvez utiliser des enregistrements nommés, et vous pouvez supprimer le commutateur, mais à la fin, il y aura toujours une sorte de Logique de commutation Mon conseil: utiliser une usine pour son usine et enregistrer l'usine dans Autofac – Steven

Répondre

23

Réponse courte: Oui. Réponse plus longue: Tout d'abord, dans les cas simples où une classe Foo est enregistrée comme implémentation pour IFoo, les paramètres du constructeur ou les propriétés de type Func<IFoo> seront automatiquement résolus par Autofac, sans nécessiter de câblage supplémentaire. Autofac va injecter un délégué qui exécute essentiellement container.Resolve<IFoo>() lorsqu'il est appelé.

Dans les cas plus complexes comme le vôtre, où la concrétion exacte renvoyée est basée sur les paramètres d'entrée, vous pouvez faire l'une des deux choses suivantes. D'abord, vous pouvez enregistrer une méthode de fabrication comme valeur de retour pour fournir une résolution paramétrées:

builder.Register<IElement>((c, p) => { 
    var dom= p.Named<IHtml>("dom"); 
    switch (dom.ElementType) 
    { 
     case "table": 
      return new TableElement(dom); 
     case "div": 
      return new DivElement(dom); 
     case "span": 
      return new SpanElement(dom); 
    } 
    return new PassthroughElement(dom); 
    }); 

//usage 
container.Resolve<IElement>(new NamedParameter("dom", domInstance)) 

C'est le type-safe (domInstance ne sera pas vérifiée par le compilateur pour vous assurer qu'il est un iHTML), ni très nettoyer. Au lieu de cela, une autre solution consiste à enregistrer en fait la méthode d'usine comme Func:

builder.Register<Func<IHtml, IElement>>(dom => 
{ 
    switch (dom.ElementType) 
    { 
     case "table": 
      return new TableElement(dom); 
     case "div": 
      return new DivElement(dom); 
     case "span": 
      return new SpanElement(dom); 
    } 
    return new PassthroughElement(dom); 
}); 


public class NeedsAnElementFactory //also registered in AutoFac 
{ 
    protected Func<IHtml,IElement> CreateElement {get; private set;} 

    //AutoFac will constructor-inject the Func you registered 
    //whenever this class is resolved. 
    public NeedsAnElementFactory(Func<IHtml,IElement> elementFactory) 
    { 
     CreateElement = elementFactory; 
    } 

    public void MethodUsingElementFactory() 
    { 
     IHtml domInstance = GetTheDOM(); 

     var element = CreateElement(domInstance); 

     //the next line won't compile; 
     //the factory method is strongly typed to IHtml 
     var element2 = CreateElement("foo"); 
    } 
} 

Si vous voulez garder le code ElementFactory au lieu de le mettre dans le module Autofac, vous pouvez faire de la fabrique statique et inscrivez-vous dès que (Cela fonctionne particulièrement bien dans votre cas car votre méthode d'usine est trivialement statique):

public class ElementFactory 
{ 
    public static IElement Create(IHtml dom) 
    { 
     switch (dom.ElementType) 
     { 
      case "table": 
       return new TableElement(dom); 
      case "div": 
       return new DivElement(dom); 
      case "span": 
       return new SpanElement(dom); 
     } 
     return new PassthroughElement(dom); 
    } 
} 

... 

builder.Register<Func<IHtml, IElement>>(ElementFactory.Create); 
+1

Merci, c'est vraiment utile! J'espérais qu'AutoFac m'aiderait à éviter la déclaration laide du switch, mais il était là, à me regarder, pratiquement se moquer de moi, dans chacun de vos trois blocs de code :) –

+2

Eh bien, il est également possible d'utiliser des "enregistrements à clé", en spécifiant chacun des noms de type d'élément comme une clé pour une méthode de résolution pour le type d'objet correspondant. La complication dans cette situation est que tous les types de retour possibles ne sont pas associés à une clé (vous avez un cas par défaut), et le parent IHTML du nom de l'élément est nécessaire pour d'autres raisons. Il a donc semblé plus simple de prendre votre code de travail et de le configurer pour qu'AutoFac puisse l'utiliser. – KeithS

+2

L'enregistrement ne compile pas: 'builder.Register > (ElementFactory.Create);' Je m'attendrais à ce qu'il ressemble à ceci: 'builder.Register > ((contexte, paramètres) => (html => ElementFactory.Create (html))); ' –