with unity tutorial started net mvc injection dependency asp dependency-injection castle-windsor asp.net-web-api self-hosting

dependency injection - unity - Resolviendo HttpControllerContext con Castle Windsor



get started with asp net web api 2 c#) (2)

Me parece que su InlineDependenciesPropagatingDependencyResolver está enmascarando algo bastante crítico para la arquitectura de su aplicación: que uno o más de sus componentes tienen dependencias que no pueden ser resueltas de manera confiable, ni desde el contenedor ni desde un contexto dinámico.

Viola la suposición que la mayoría de los desarrolladores harían al pasar dependencias en línea a Resolve () (que solo pasen un nivel de resolución de dependencia) y en ciertos escenarios podría causar que una dependencia anule incorrectamente algún otro servicio configurado. (Por ejemplo, si tiene otro componente muchos niveles abajo que tenían una dependencia del mismo tipo y nombre). Podría ser una causa potencial de errores que serían muy difíciles de identificar.

El problema que está en el corazón de esto es difícil para DI y realmente indica que la IoC no es realmente factible (es decir, que nuestra (s) dependencia (s) deben ser ''empujadas'' y el contenedor no puede ''tirar'' de nosotros). Me parece que hay dos opciones:

1) rectificar el problema que impide la "inversión". es decir, ajuste el HttpControllerContext / HttpContext, aumente ese wrapper para que se comporte como se requiere en un escenario autohospedado y haga que sus componentes dependan de ese wrapper, en lugar de HttpControllerContext / HttpContext directamente.

2) reflejan las deficiencias del entorno con el que está trabajando (que no admite completamente la "inversión") y hacen que la solución para manejar esas deficiencias sea muy explícita. En su escenario, esto probablemente implicaría utilizar una fábrica de baseUri (interfaz) para crear una instancia del componente que requiere una baseUri en su IHttpControllerActivator.Create() . Esto significaría que si este componente estuviera más abajo en la jerarquía de dependencias, necesitaría construir explícitamente su jerarquía de dependencias hasta que tuviera su controlador.

Probablemente iría por la segunda opción simplemente porque cuando las convenciones no se cortan, prefiero ser lo más explícito posible.

ACTUALIZADO Suponiendo que tuviéramos un controlador tipo A , que dependía del componente B , que a su vez dependía de la baseUri, la segunda opción podría tener el siguiente aspecto:

// 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); }

Los puntos clave son que esto trata explícitamente las deficiencias de nuestro entorno, ata los tipos afectados específicamente con las anulaciones de dependencia que proporcionamos imperativamente y asegura que no estamos anulando accidentalmente ninguna otra dependencia.

En la API web ASP.NET , las instancias HttpControllerContext proporcionan mucha información sobre el entorno actual, incluido el URI de la solicitud actual.

Si un servicio se basa en dicha información (por ejemplo, el URI de solicitud), debería ser posible inyectar esa información en el servicio.

Esto es bastante fácil de hacer usando la DI de Poor Man: solo implemente un IHttpControllerActivator personalizado .

Sin embargo, con Castle Windsor esto de repente se vuelve muy difícil. Anteriormente, describí una forma muy complicada de resolver este problema, pero depende del estilo de vida PerWebRequest, y resulta que este estilo de vida no funciona en escenarios de autohospedaje, porque HttpContext.Current está vacío.

Hasta ahora, he podido hacer que esto funcione pasando la información deseada como un argumento en línea al método Resolve desde un IHttpControllerActivator personalizado:

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 }); }

Sin embargo, de manera predeterminada, esto solo funciona si el tipo solicitado inmediatamente depende del argumento (es decir, si el Controlador solicitado depende de la baseUri ). Si la dependencia de baseUri está enterrada más profundamente en la jerarquía de dependencias, no funciona de manera predeterminada, porque los argumentos en línea no se propagan a capas más profundas.

Este comportamiento se puede cambiar con un IDependencyResolver personalizado (un Castle Windsor IDependencyResolver, no un ASP.NET Web API IDependencyResolver):

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

Observe que true se pasa como el argumento del constructor propagateInlineDependencies lugar de false , que es la implementación predeterminada.

Para conectar una instancia de contenedor con la clase InlineDependenciesPropagatingDependencyResolver, debe construirse de esta manera:

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

Me pregunto si esta es la mejor solución para este problema, o si hay una forma mejor / más simple.


Solo para completar, una respuesta que obtuve de Krzysztof Koźmic (el actual mantenedor de Castle Windsor) en Twitter indicó que el método esbozado en la pregunta es, de hecho, la forma correcta de lograr este objetivo en particular.

(Sin embargo, no puedo vincular a ese tweet, ya que la cuenta de Twitter de Krzysztof está protegida (los tweets no son públicamente visibles))