windsor net asp.net inversion-of-control castle-windsor

asp.net - net - castle windsor github



¿Cómo usar Castle Windsor con los formularios web ASP.Net? (5)

Creo que básicamente estás en el camino correcto: si no lo has hecho, te sugiero que veas Rhino Igloo, un framework MVC de WebForms. Aquí hay una buena publicación en el blog y la fuente está aquí . Ayende (el autor de Rhino) Igloo) aborda el tema del uso de Windsor con webforms bastante bien en este proyecto / biblioteca.

Guardaría en caché la información de reflexión si vas a inyectar todo el conjunto de controles anidados, que podría terminar siendo un poco de un cerdo de rendimiento que sospecho.

Lo último de todo spring.net aborda esto de una manera más orientada a la configuración, pero podría valer la pena echarle un vistazo a su implementación: aquí hay una buena publicación de referencia sobre este tema.

Estoy intentando conectar la inyección de dependencia con Windsor a los formularios web asp.net estándar. Creo que lo he logrado utilizando un HttpModule y un CustomAttribute (código que se muestra a continuación), aunque la solución parece un poco torpe y me preguntaba si existe una mejor solución compatible con Windsor.

Hay varios archivos que se muestran juntos aquí

// index.aspx.cs public partial class IndexPage : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Logger.Write("page loading"); } [Inject] public ILogger Logger { get; set; } } // WindsorHttpModule.cs public class WindsorHttpModule : IHttpModule { private HttpApplication _application; private IoCProvider _iocProvider; public void Init(HttpApplication context) { _application = context; _iocProvider = context as IoCProvider; if(_iocProvider == null) { throw new InvalidOperationException("Application must implement IoCProvider"); } _application.PreRequestHandlerExecute += InitiateWindsor; } private void InitiateWindsor(object sender, System.EventArgs e) { Page currentPage = _application.Context.CurrentHandler as Page; if(currentPage != null) { InjectPropertiesOn(currentPage); currentPage.InitComplete += delegate { InjectUserControls(currentPage); }; } } private void InjectUserControls(Control parent) { if(parent.Controls != null) { foreach (Control control in parent.Controls) { if(control is UserControl) { InjectPropertiesOn(control); } InjectUserControls(control); } } } private void InjectPropertiesOn(object currentPage) { PropertyInfo[] properties = currentPage.GetType().GetProperties(); foreach(PropertyInfo property in properties) { object[] attributes = property.GetCustomAttributes(typeof (InjectAttribute), false); if(attributes != null && attributes.Length > 0) { object valueToInject = _iocProvider.Container.Resolve(property.PropertyType); property.SetValue(currentPage, valueToInject, null); } } } } // Global.asax.cs public class Global : System.Web.HttpApplication, IoCProvider { private IWindsorContainer _container; public override void Init() { base.Init(); InitializeIoC(); } private void InitializeIoC() { _container = new WindsorContainer(); _container.AddComponent<ILogger, Logger>(); } public IWindsorContainer Container { get { return _container; } } } public interface IoCProvider { IWindsorContainer Container { get; } }


En lugar de hacerlo así, también podría usar un tipo resolver directamente con algo como:

ILogger Logger = ResolveType.Of<ILogger>();


Recientemente comencé en una empresa donde hay muchas aplicaciones heredadas de formularios web, por lo que este parece ser un enfoque realmente interesante, y podría ofrecer un camino a seguir si quisiéramos agregar DI a las páginas web existentes, gracias.

Un punto que noté es que el método de inyección usa el contenedor. Resuelva para resolver los componentes explícitamente, por lo tanto, creo que es posible que necesitemos hacer un contenedor. Libere los componentes cuando la página se descargue.

Si tenemos componentes transitorios y no hacemos esto, entonces podemos enfrentar fugas de memoria. No estoy seguro de cómo se comportarían los componentes con el estilo de vida de Per Web Request (es decir, Windsor los recogería al final de la solicitud web, aunque los hayamos resuelto explícitamente), pero también es posible que aquí quiera jugar a salvo.

Por lo tanto, puede ser necesario ampliar el módulo para realizar un seguimiento de los componentes que resuelve y soltarlos para que Windsor sepa cuándo realizar la limpieza.


Aquí hay una versión modificada del código del OP que (i) almacena en caché las propiedades inyectadas para evitar llamadas de reflexión repetidas, (ii) libera todos los componentes resueltos, (iii) encapsula el acceso al contenedor para no exponer la implementación.

// global.asax.cs public class Global : HttpApplication { private static IWindsorContainer _container; protected void Application_Start(object sender, EventArgs e) { _container = new WindsorContainer(); _container.Install(FromAssembly.This()); } internal static object Resolve(Type type) { return _container.Resolve(type); } internal static void Release(object component) { _container.Release(component); } //... } // WindsorHttpModule.cs public class WindsorHttpModule : IHttpModule { // cache the properties to inject for each page private static readonly ConcurrentDictionary<Type, PropertyInfo[]> InjectedProperties = new ConcurrentDictionary<Type, PropertyInfo[]>(); private HttpApplication _context; public void Init(HttpApplication context) { _context = context; _context.PreRequestHandlerExecute += InjectProperties; _context.EndRequest += ReleaseComponents; } private void InjectProperties(object sender, EventArgs e) { var currentPage = _context.Context.CurrentHandler as Page; if (currentPage != null) { InjectProperties(currentPage); currentPage.InitComplete += delegate { InjectUserControls(currentPage); }; } } private void InjectUserControls(Control parent) { foreach (Control control in parent.Controls) { if (control is UserControl) { InjectProperties(control); } InjectUserControls(control); } } private void InjectProperties(Control control) { ResolvedComponents = new List<object>(); var pageType = control.GetType(); PropertyInfo[] properties; if (!InjectedProperties.TryGetValue(pageType, out properties)) { properties = control.GetType().GetProperties() .Where(p => p.GetCustomAttributes(typeof(InjectAttribute), false).Length > 0) .ToArray(); InjectedProperties.TryAdd(pageType, properties); } foreach (var property in properties) { var component = Global.Resolve(property.PropertyType); property.SetValue(control, component, null); ResolvedComponents.Add(component); } } private void ReleaseComponents(object sender, EventArgs e) { var resolvedComponents = ResolvedComponents; if (resolvedComponents != null) { foreach (var component in ResolvedComponents) { Global.Release(component); } } } private List<object> ResolvedComponents { get { return (List<object>)HttpContext.Current.Items["ResolvedComponents"]; } set { HttpContext.Current.Items["ResolvedComponents"] = value; } } public void Dispose() { } }


Una cosa que faltaba en las respuestas aceptadas era el hecho de que el módulo http debe registrarse en el archivo web.config (dependiendo de la aplicación) antes de que el módulo resuelva realmente las dependencias en las páginas de código subyacente. Lo que necesitas es:

<system.webServer> <modules> <add name="ClassNameForHttpModuleHere" type="NamespaceForClass"/> </modules> </system.webServer>

Aparte de eso, las soluciones aceptadas funcionaron como un encanto.

Referencia al sitio web de Microsoft para agregar módulos http: https://msdn.microsoft.com/en-us/library/ms227673.aspx