27 votes

La résolution de HttpControllerContext avec le Château de Windsor

Dans le ASP.NET l'API Web, HttpControllerContext cas, fournir beaucoup d'informations sur l'environnement actuel, y compris l'URI de la requête courante.

Si un service s'appuie sur ces informations (par exemple, l'URI de la demande), il devrait être possible d'injecter de l'information dans le service.

C'est assez facile à faire en utilisant du Pauvre DI: il suffit de mettre en œuvre une coutume IHttpControllerActivator.

Cependant, avec le Château de Windsor, cela devient tout à coup très difficile. Auparavant, j'ai décrit un très alambiqué façon de résoudre ce problème, mais il dépend de la PerWebRequest mode de vie, et il s'avère que ce mode de vie ne fonctionne pas dans l'auto-hébergement, les scénarios, parce HttpContext.Le courant est vide.

Jusqu'à présent, j'ai été en mesure de faire ce travail en transmettant l'information désirée en ligne argument pour la détermination de la méthode à partir d'un personnalisé IHttpControllerActivator:

public IHttpController Create(
    HttpControllerContext controllerContext,
    Type controllerType)
{
    var baseUri = new Uri(
        controllerContext
            .Request
            .RequestUri
            .GetLeftPart(UriPartial.Authority));

    return (IHttpController)this.container.Resolve(
        controllerType,
        new { baseUri = baseUri });
}

Toutefois, par défaut, cela fonctionne uniquement si la demande immédiatement à ce type s'appuie sur l'argument (c'est à dire si la manette elle-même dépend de l' baseUri). Si la dépendance à l'égard baseUri est enterré profondément dans la dépendance de la hiérarchie, il ne fonctionne pas par défaut, parce que inline arguments ne sont pas propagées aux couches les plus profondes.

Ce comportement peut être changé avec une coutume IDependencyResolver (un Château de Windsor IDependencyResolver, pas un ASP.NET l'API Web IDependencyResolver):

public class InlineDependenciesPropagatingDependencyResolver :
    DefaultDependencyResolver
{
    protected override CreationContext RebuildContextForParameter(
        CreationContext current, Type parameterType)
    {
        if (parameterType.ContainsGenericParameters)
        {
            return current;
        }

        return new CreationContext(parameterType, current, true);
    }
}

Notez que true est passée comme l' propagateInlineDependencies argument du constructeur au lieu de false, ce qui est la valeur par défaut de mise en œuvre.

Afin d'associer une instance de conteneur avec le InlineDependenciesPropagatingDependencyresolver classe, il doit être construit de cette façon:

this.container = 
    new WindsorContainer(
        new DefaultKernel(
            new InlineDependenciesPropagatingDependencyResolver(),
            new DefaultProxyFactory()),
        new DefaultComponentInstaller());

Je me demande si c'est la meilleure solution à ce problème, ou si il y a un mieux/plus simple?

2voto

Phil Degenhardt Points 3535

Il me semble que votre InlineDependenciesPropagatingDependencyresolver est en fait de masquage quelque chose d'assez critique à l'architecture de votre application: que l'un ou plusieurs de vos composants ont des dépendances qui ne peuvent pas être correctement résolus de manière statique, à partir du conteneur, ou de contexte dynamique.

Il viole l'hypothèse la plupart des développeurs de faire lors du passage inline dépendances à Résoudre() (qu'ils ne sont transmises à un niveau de résolution des dépendances) et dans certains scénarios pourrait provoquer une dépendance à tort remplacer certains autres configuré service. (E. g. si vous aviez un autre composant de nombreux niveaux qui avaient une dépendance de même type et de même nom). Il pourrait être une cause potentielle de bugs qui seraient très difficiles à identifier.

La question au cœur de ce qui est difficile pour les DI et indique que le Cio n'est pas vraiment possible (c'est à dire notre dépendance(s) besoin d'être "poussé" et ne peut pas être "tiré" pour nous par le conteneur). Il me semble qu'il existe deux options:

1) corriger le problème à la prévention de la "inversion". c'est à dire envelopper le HttpControllerContext/HttpContext, compléter ce wrapper pour se comporter comme nécessaire dans une auto-hébergé scénario et avez vos composants reposent sur ce wrapper, plutôt que de HttpControllerContext/HttpContext directement.

2) réfléchir sur les carences de l'environnement dans lequel vous travaillez (qu'il ne prend pas entièrement en charge les "inversion") et de rendre la solution de contournement pour traiter ces problèmes de manière très explicite. Dans votre scénario, il faudrait probablement en utilisant un typée usine (interface) pour instancier le composant nécessitant un baseUri votre IHttpControllerActivator.Create(). Cela signifie que si ce composant est plus bas dans la hiérarchie de dépendance, vous devez explicitement construire votre dépendance de la hiérarchie jusqu'à ce que vous avez eu votre contrôleur.

Je serais probablement aller pour la deuxième option tout simplement parce que, lorsque les conventions ne coupe pas, je préfère être aussi explicite que possible.

Mise à JOUR En supposant que nous avons eu un type de contrôleur A, qui s'est fondé sur les composants B, qui à son tour a invoqué le baseUri, le deuxième option pourrait ressembler à quelque chose comme:

// Typed factories for components that have dependencies, which cannot be resolved statically
IBFactory bFactory; 
IAFactory aFactory;

public IHttpController Create(HttpControllerContext controllerContext, Type controllerType)
{
    if (controllerType == typeof(A))
    {
        // Special handling for controller where one or more dependencies
        // are only available via controllerContext.
        var baseUri = new Uri(controllerContext.Request.RequestUri.GetLeftPart(UriPartial.Authority));
        B b = this.bFactory.Create(baseUri);
        return this.aFactory.Create(b);
    }
    // Default for all other controllers 
    return (IHttpController)this.container.Resolve(controllerType);
}

Les points clés de cette traite explicitement les insuffisances de notre environnement, il se lie touchées types spécifiquement à la dépendance des remplacements de nous fournir impérativement et s'assure que nous ne sommes pas accidentellement ignorant tous les autres dépendances.

2voto

Mark Seemann Points 102767

Juste par souci d'exhaustivité, une réponse que j'ai reçu de Krzysztof Koźmic (le mainteneur actuel du Château de Windsor) sur Twitter a indiqué que la méthode décrite dans la question est, en effet, la bonne façon d'atteindre cet objectif en particulier.

(Cependant, je ne peux pas lier à ce tweet, depuis Krzysztof twitter, le compte est protégé (les tweets ne sont pas visibles par le public.))

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X