web-dev-qa-db-fra.com

Ioc / DI - Pourquoi dois-je référencer toutes les couches / assemblages dans le point d'entrée de l'application?

(Lié à cette question, EF4: Pourquoi la création de proxy doit-elle être activée lorsque le chargement différé est activé? ).

Je suis nouveau à DI, alors restez avec moi. Je comprends que le conteneur est en charge de l'instanciation de tous mes types enregistrés, mais pour ce faire, il nécessite une référence à toutes les DLL de ma solution et à leurs références.

Si je n'utilisais pas de conteneur DI, je n'aurais pas à référencer la bibliothèque EntityFramework dans mon application MVC3, uniquement ma couche métier, qui ferait référence à ma couche DAL/Repo.

Je sais qu'à la fin de la journée, toutes les DLL sont incluses dans le dossier bin mais mon problème est de devoir le référencer explicitement via "ajouter une référence" dans VS afin de pouvoir publier un WAP avec tous les fichiers nécessaires.

112
diegohb

Si je n'utilisais pas de conteneur DI, je n'aurais pas à référencer la bibliothèque EntityFramework dans mon application MVC3, uniquement ma couche métier qui ferait référence à ma couche DAL/Repo.

Oui, c'est exactement la situation que DI travaille si dur à éviter :)

Avec du code étroitement couplé, chaque bibliothèque ne peut avoir que quelques références, mais celles-ci ont à nouveau d'autres références, créant un graphique détaillé des dépendances, comme ceci:

Deep Graph

Étant donné que le graphique des dépendances est profond, cela signifie que la plupart des bibliothèques glissent sur de nombreuses autres dépendances, par exemple dans le diagramme, Bibliothèque C traîne le long Bibliothèque H, Bibliothèque E, Bibliothèque J, Bibliothèque M, Bibliothèque K et Bibliothèque N. Cela rend plus difficile la réutilisation de chaque bibliothèque indépendamment des autres - par exemple lors des tests unitaires .

Cependant, dans une application faiblement couplée, en déplaçant toutes les références au Racine de composition , le graphique de dépendance est sévèrement aplati :

Shallow Graph

Comme illustré par la couleur verte, il est désormais possible de réutiliser Bibliothèque C sans faire glisser les dépendances indésirables.

Cependant, tout cela dit, avec de nombreux conteneurs DI, vous n'avez pas avez pour ajouter des références matérielles à toutes les bibliothèques requises. Au lieu de cela, vous pouvez utiliser la liaison tardive sous la forme d'une numérisation d'assemblage basée sur des conventions (préférée) ou d'une configuration XML.

Lorsque vous faites cela, cependant, vous devez vous rappeler de copier les assemblys dans le dossier bin de l'application, car cela ne se produit plus automatiquement. Personnellement, je trouve rarement que cela vaut cet effort supplémentaire.

Une version plus élaborée de cette réponse se trouve dans cet extrait de mon livre Injection de dépendance, principes, pratiques, modèles .

185
Mark Seemann

Si je n'utilisais pas de conteneur DI, je n'aurais pas à référencer la bibliothèque EntityFramework dans mon application MVC3

Même lorsque vous utilisez un conteneur DI, vous n'avez pas à laisser votre projet MVC3 référencer EF, mais vous (implicitement) choisissez de le faire en implémentant Composition Root (le chemin de démarrage où vous composez votre objet graphiques) dans votre projet MVC3. Si vous êtes très strict quant à la protection de vos limites architecturales à l'aide d'assemblages, vous pouvez déplacer votre racine de composition ou votre présentation (MVC) vers une bibliothèque de classes.

Dans la première option, vous permettez à votre projet MVC3 de référencer cet assemblage 'bootstrapper' séparé, et il référencera tous les autres assemblys de votre solution et référencera votre bibliothèque de conteneurs DI. Le problème avec cela est que ce projet d'amorçage ne peut pas référencer les types situés dans le projet MVC3 (car cela entraînera une dépendance d'assemblage cyclique). Ces types doivent être déplacés vers le projet d'amorçage (qui doit éventuellement faire référence à System.Web.Mvc), ou vous devez conserver une petite partie de la configuration du conteneur dans votre application MVC3. Notez également que votre projet MVC fait toujours référence indirectement à tous les autres assemblys via le nouvel assembly bootstrapper, car les dépendances d'assembly sont transitives.

Bien que placer la racine de composition dans un assembly séparé soit une chose valide à faire, la plupart des puristes DI (y compris moi) ne déplacent généralement la racine de composition vers une bibliothèque de classes que lorsqu'il existe plusieurs applications finales (c'est-à-dire une application Web + un service Web + un service Windows ) qui utilisent la même couche métier. Lorsque j'ai une seule application, je garde la racine de composition dans mon application finale.

La deuxième option consiste à déplacer toutes les classes liées à MVC (vues, contrôleurs, etc.) du projet de démarrage vers une bibliothèque de classes. Cela permet à ce nouvel assemblage de couche de présentation de rester déconnecté du reste de l'application. Votre projet d'application Web lui-même deviendra un shell très fin avec la logique de démarrage requise. Le projet d'application Web sera la racine de composition qui référence tous les autres assemblys.

Extraire la logique de présentation dans une bibliothèque de classes peut compliquer les choses lorsque vous travaillez avec MVC. Il sera plus difficile de tout câbler, car les contrôleurs et les vues, les images, les fichiers css, etc. ne sont pas dans le projet de démarrage. C'est probablement faisable mais cela prendra plus de temps à installer.

Les deux options ont leurs inconvénients et c'est pourquoi je conseille généralement de simplement conserver la racine de composition dans le projet Web. De nombreux développeurs ne veulent pas que leur assemblage MVC dépende de l'assemblage DAL, mais ce n'est pas vraiment un problème. N'oubliez pas que les assemblys sont un déploiement artefact; vous divisez le code en plusieurs assemblys pour permettre au code d'être déployé séparément. En revanche, une couche architecturale est un artefact logique . Il est très possible (et courant) d'avoir plusieurs couches dans le même assemblage.

Dans ce cas, nous finirons par avoir la racine de composition (couche) et la couche de présentation dans le même projet d'application Web (donc dans le même assemblage). Et même si cet assembly fait référence à l'assembly contenant le DAL, la couche Présentation ne fait toujours pas référence à la couche d'accès aux données . C'est une grande distinction.

Bien sûr, lorsque nous faisons cela, nous perdons la possibilité pour le compilateur de vérifier cette règle architecturale au moment de la compilation, mais cela ne devrait pas être un problème. La plupart des règles architecturales ne peuvent pas être vérifiées par le compilateur et il y a toujours quelque chose comme du bon sens. Et s'il n'y a pas de bon sens dans votre équipe, vous pouvez toujours utiliser des revues de code (ce que chaque équipe devrait IMO toujours faire btw). Vous pouvez également utiliser un outil tel que NDepend (qui est commercial), qui vous aide à vérifier vos règles architecturales. Lorsque vous intégrez NDepend à votre processus de génération, il peut vous avertir lorsque quelqu'un a archivé du code qui viole une telle règle architecturale.

Vous pouvez lire une discussion plus élaborée sur le fonctionnement de la racine de composition dans le chapitre 4 de mon livre Injection de dépendance, principes, pratiques, modèles .

63
Steven

Si je n'utilisais pas de conteneur DI, je n'aurais pas à référencer la bibliothèque EntityFramework dans mon application MVC3, uniquement ma couche métier qui ferait référence à ma couche DAL/Repo.

Vous pouvez créer un projet séparé appelé "DependencyResolver". Dans ce projet, vous devez référencer toutes vos bibliothèques.

Maintenant, la couche UI n'a pas besoin de NHibernate/EF ou de toute autre bibliothèque non pertinente pour l'interface utilisateur, à l'exception de Castle Windsor pour être référencée.

Si vous souhaitez masquer Castle Windsor et DependencyResolver de votre couche d'interface utilisateur, vous pouvez écrire un HttpModule qui appelle les éléments du registre IoC.

Je n'ai qu'un exemple pour StructureMap:

public class DependencyRegistrarModule : IHttpModule
{
    private static bool _dependenciesRegistered;
    private static readonly object Lock = new object();

    public void Init(HttpApplication context)
    {
        context.BeginRequest += (sender, args) => EnsureDependenciesRegistered();
    }

    public void Dispose() { }

    private static void EnsureDependenciesRegistered()
    {
        if (!_dependenciesRegistered)
        {
            lock (Lock)
            {
                if (!_dependenciesRegistered)
                {
                    ObjectFactory.ResetDefaults();

                    // Register all you dependencies here
                    ObjectFactory.Initialize(x => x.AddRegistry(new DependencyRegistry()));

                    new InitiailizeDefaultFactories().Configure();
                    _dependenciesRegistered = true;
                }
            }
        }
    }
}

public class InitiailizeDefaultFactories
{
    public void Configure()
    {
        StructureMapControllerFactory.GetController = type => ObjectFactory.GetInstance(type);
          ...
    }
 }

DefaultControllerFactory n'utilise pas directement le conteneur IoC, mais il délègue aux méthodes de conteneur IoC.

public class StructureMapControllerFactory : DefaultControllerFactory
{
    public static Func<Type, object> GetController = type =>
    {
        throw new  InvalidOperationException("The dependency callback for the StructureMapControllerFactory is not configured!");
    };

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            return base.GetControllerInstance(requestContext, controllerType);
        }
        return GetController(controllerType) as Controller;
    }
}

Le délégué GetController est défini dans un registre StructureMap (à Windsor, il doit s'agir d'un programme d'installation).

5
Rookian
  • Il y a une dépendance: si un objet instancie un autre objet.
  • Il n'y a pas de dépendance: si un objet attend une abstraction (injection de constructeur, injection de méthode ...)
  • Les références d'assembly (référencement dll, webservices ..) sont indépendantes du concept de dépendance, car pour résoudre une abstraction et pouvoir compiler le code, la couche doit la référencer.
0
riadh gomri