2012-01-20 32 views
13

Estoy en el proceso de introducir un marco de inyección de dependencias en una aplicación WebForms existente (usando Castle Windsor).Webforms and Dependency Injection

Tengo una experiencia bastante profunda con DI, y tiendo a favorecer fuertemente la inyección del constructor sobre la inyección del colocador. Si está familiarizado con Webforms, sabrá que el marco ASP.Net maneja la construcción de objetos de página y control, lo que imposibilita la verdadera inyección de constructor.

Mi solución actual es registrar el contenedor en el evento Application_Start de Global.asax, y mantener el contenedor como una variable estática pública en Global también. Luego simplemente resuelvo cada servicio que necesito directamente en la página o control cuando los necesito. Así que en la parte superior de cada página, termino con un código como éste:

private readonly IMyService _exposureManager = Global.IoC.Resolve<IMyService>(); 
private readonly IMyOtherService _tenCustomersExposureManager = Global.IoC.Resolve<IMyOtherService>(); 

Obviamente, no me gusta tener todas estas referencias al contenedor esparcidos sobre mi solicitud o tener mi página Dependencias/control no sean -explícito, pero no he podido encontrar una mejor manera.

¿Existe una solución más elegante para usar DI con Webforms?

+0

Encontré este artículo http://www.codemag.com/Article/1210031 que tiene un código de muestra excelente sobre cómo implementar la inyección de dependencia en WPF, MVC y WebForms (usando 'PageHandlerFactory', en el caso de WebForms) . Parece decir que debido a las limitaciones de clase de código subyacente, tendrá que usar la inyección setter. También, curiosamente, muestra cómo Microsoft Managed Extensibility Framework (MEF) puede ayudarlo a resolver este y otros problemas DI similares de una manera muy útil y poco estándar. –

Respuesta

16

Estoy de acuerdo con @DarinDimitrov que MVP es una opción interesante. Sin embargo, cuando se trabaja con una aplicación heredada, la reescritura de una página existente para el patrón MVP es un trabajo tremendo. En ese caso, podría ser mejor comenzar con el patrón Localizador de servicios (pero solo en las clases de la UI) como ya lo está haciendo. Sin embargo, cambie una cosa. No exponga el contenedor DI elegido a la aplicación, como supongo que está haciendo con la propiedad Global.IoC.

En su lugar, cree un método estático Resolve<T> en la clase Global. Esto oculta completamente el contenedor y le permite intercambiar implementaciones sin tener que cambiar nada en sus páginas web. Cuando hace esto, no hay ninguna ventaja al utilizar el Localizador de servicios comunes como lo propone @Wiktor. Common Service Locator es solo otra abstracción para algo que no tiene que ser abstraído (ya que usted ya ha abstraído el contenedor usando el Global.Resolve<T>).

Desafortunadamente con los formularios web, no hay realmente ninguna buena manera de hacerlo. Para Simple Injector, escribí un integration guide for Web Forms que básicamente describe el uso del método Global.Resolve<T>, pero también muestra una forma de comprobar si se pueden crear clases de página. La guía también se puede usar para otros contenedores DI.

Por cierto, tenga en cuenta que con Castle Windsor, todo lo que solicite debe ser liberado explícitamente (el Register Resolve Release pattern). Esto es un poco desagradable (IMO) y difiere de cómo funcionan otros contenedores y puede ser una fuente de pérdidas de memoria cuando no lo hace correctamente.

Última nota. It is possible to do constructor injection with Web Forms. Bueno ... más o menos, ya que esto llamará al constructor sobrecargado utilizando la reflexión después de que se haya creado el Form usando el constructor predeterminado, por lo que esto causa Temporal Coupling.

+0

Gracias por su respuesta. Ese último artículo es interesante, y de hecho sería de gran interés si no fuera por el problema de la plena confianza. –

+0

@PhilSandler: Parece que Microsoft ha [abandonado por completo la confianza parcial de ASP.NET 4.0] (https://stackoverflow.com/questions/16849801/is-trying-to-develop-for-medium-trust-a-lost- causa) y más allá. – Steven

+0

Si el método 'Resolver ' es estático, entonces no tiene acceso al contenedor de miembro de instancia. Aunque podría estar malentendiéndote. ¿Recomiendas también hacer el contenedor estático? –

4

¿Existe una solución más elegante para usar DI con Webforms?

Yeap, el MVP pattern le permite tener una clara separación de preocupaciones en una aplicación WebForms. Y una vez que tiene separación de preocupaciones y un acoplamiento débil, DI es fácil.

Y en ASP.NET MVC que está incorporado.

0

En realidad, lo que acaba de crear es su propia implementación del Localizador de servicios. Pero, casi con seguridad, ya existe una implementación para un marco de trabajo de IoC de su elección.

http://commonservicelocator.codeplex.com/

2

ASP.NET MVC tiene IDependencyResolver y una static manager class que le permite obtener y establecer el sistema de resolución. No me gustaba la idea de hacer referencia System.Web.Mvc en un proyecto de formularios web, así que fui con IServiceLocator, lo que hace de la misma cosa:

public static class Bootstrapper 
{ 
    private static readonly IUnityContainer _container = new UnityContainer(); 

    public static void Initialize() 
    { 
     ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(_container)); 

     _container.RegisterType<IDriverService, DriverService>(); 
    } 

    public static void TearDown() 
    { 
     _container.Dispose(); 
    } 
} 

public class Global : HttpApplication 
{ 
    protected void Application_Start(object sender, EventArgs e) 
    { 
     Bootstrapper.Initialize(); 
    } 

    protected void Application_End(object sender, EventArgs e) 
    { 
     Bootstrapper.TearDown(); 
    } 
} 

Luego, en su clase de página ...

IDriverService service = ServiceLocator.Current.GetInstance<IDriverService>(); 

O wire up DI via constructor injection. Todavía no he recorrido ese camino con formularios web, por lo que alguien más tendrá que reemplazarlo :) (He estado viviendo principalmente en tierras de MVC durante aproximadamente un año).

Mi ejemplo usa Unity, pero debería ser capaz de adaptarlo a cualquier otra implementación de DI con bastante facilidad.

+0

¿Es realmente útil hacer un 'TearDown' al final de la aplicación? El dominio de la aplicación se está descargando de todos modos. Solo es útil si registró servicios singleton que tienen lógica de extracción en su método 'Dispose', pero aún sería frágil, ya que no hay garantía de que todos esos métodos' Dispose' se ejecuten realmente cuando se descarga un AppDomain. – Steven

+0

Buena pregunta. Simplemente estaba tratando de seguir el patrón desechable. No estaba seguro de dónde más ponerlo aparte de la aplicación. – jrummell

1

Como @DarinDimitrov dice que el patrón de MVP es el camino a seguir para usar DI/IOC con Webforms.

O bien puede implementar su propia implementación o utilizar un marco existente. Escuché bueno sobre Webforms MVP, pero en realidad no lo he usado.

Según the docs, se ha incorporado el soporte para DI a través de Castle Windsor, Autofac y Unity. También tiene auto descubrimiento basado en convenciones para presentadores.

Cuestiones relacionadas