c# asp.net dependency-injection webforms castle-windsor

c# - Formas web y inyección de dependencia



asp.net dependency-injection (6)

Estoy en el proceso de introducir un marco de Inyección de Dependencia en una aplicación de WebForms existente (usando Castle Windsor).

Tengo una experiencia bastante profunda con DI, y tiendo a favorecer fuertemente la inyección de constructor sobre la inyección de incubadora. Si está familiarizado con los formularios web, sabe que el marco de ASP.Net se encarga de la construcción de los objetos de página y control, lo que hace imposible la verdadera inyección de constructores.

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

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

Obviamente, no me gusta que todas estas referencias al contenedor repartidas sobre mi aplicación o que las dependencias de mi página / control no sean explícitas, pero no he podido encontrar una mejor manera.

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


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

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

Y en ASP.NET MVC está incorporado.


ASP.NET MVC tiene IDependencyResolver y una clase de administrador estático que le permite obtener y configurar la resolución. No me gustó la idea de hacer referencia a System.Web.Mvc en un proyecto de formularios web, así que IServiceLocator por IServiceLocator , que hace casi lo mismo:

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 tu clase de página ...

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

O cablear DI mediante inyección de constructor. Todavía no he ido por ese camino con formularios web, por lo que alguien más tendrá que llenarme por mí :) (He estado viviendo en la mayoría de los casos en MVC durante aproximadamente un año).

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


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

O bien puede rodar su propia implementación o usar un marco existente. He oído hablar de los formularios web de MVP , pero en realidad no lo he usado.

Según los documentos , ha incorporado soporte para DI a través de Castle Windsor, Autofac y Unity. También tiene el descubrimiento automático basado en convenciones para los presentadores.


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

IServiceLocator


Estoy de acuerdo con @DarinDimitrov en que MVP es una opción interesante. Sin embargo, cuando se trabaja con una aplicación heredada, volver a escribir una página existente con el patrón MVP es un trabajo fantástico. En ese caso, podría ser mejor comenzar con el patrón del Localizador de servicios (pero solo en sus clases de UI) como ya está haciendo. Sin embargo, cambiar una cosa. No exponga el contenedor de DI elegido a la aplicación, como espero 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 el contenedor por completo y le permite intercambiar implementaciones sin tener que cambiar nada en sus páginas web. Al hacer esto, no hay ninguna ventaja en el uso del Localizador de servicios común como propone @Wiktor. El Localizador de servicios comunes es solo una abstracción más para algo que no tiene que ser abstraído (ya que usted ya ha retirado el contenedor utilizando Global.Resolve<T> ).

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

Por cierto, tenga en cuenta que con Castle Windsor, todo lo que solicite debe publicarse explícitamente (el patrón de liberación de resolución de registro ). 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.

Ultima nota Es posible realizar inyecciones de constructor con Web Forms . Bueno ... más o menos, ya que esto llamará al constructor sobrecargado usando la reflexión después de que se haya creado el Form usando el constructor predeterminado, por lo que esto causa el acoplamiento temporal .


Sepa que esto es bastante antiguo, pero ahora, hay DI en WebForms comenzando en .NET 4.7.2. Con respecto a este artículo: Blog de ASP.NET: Use la inyección de dependencia en la aplicación WebForms

Simplemente instale Microsoft.AspNet.WebFormsDependencyInjection.Unity paquete Microsoft.AspNet.WebFormsDependencyInjection.Unity y registre sus clases en Global.asax :

protected void Application_Start(object sender, EventArgs e) { var container = this.AddUnity(); container.RegisterType<IPopularMovie, MovieManager>(); container.RegisterType<IMovieRepository, XmlMovieRepository>(); }

Espero que te ayude.