2008-11-16 55 views
32

我正试图将依赖注入与Windsor连接到标准的asp.net web表单。我想我已经使用HttpModule和CustomAttribute(下面显示的代码)实现了这一点,尽管解决方案似乎有点笨拙,并且想知道Windsor是否有更好的受支持的解决方案?如何使用Castle Windsor与ASP.Net Web窗体?

有几个文件都显示一起在这里

// 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; } 
    } 
+0

只想说感谢上面它让我创造了一些传统的web表单代码的MVP框架的代码。 – 2009-03-18 10:06:24

+0

基思没有问题..很高兴它可能对某人有用 – Xian 2009-03-19 21:13:18

+6

我试过这段代码,但实际上每次请求都会清除ViewState。看起来,如果在Load事件之前访问当前页面的Controls属性,则ASP.NET无法在Init和Load之间的LoadViewState阶段恢复ViewState(请参阅forums.asp.net/p/1043999/1537884。 ASPX)。我相信这就是为什么Ayende分别在页面,母版页和用户控件的基类中使用Init事件来解决任何IoC依赖性的原因。 – gabe 2009-09-28 18:57:13

回答

16

我想你基本上是正确的轨道上 - 如果你还没有准备好,我建议考虑看看犀牛冰屋,一的WebForms MVC框架, Here's a good blog post on this和来源是here - Ayende(Rhino Igloo的作者)解决了在这个项目/库中使用Windsor与webforms相当好的问题。

如果你要注入整个嵌套控件集合,我可能会缓存反射信息,这可能最终会成为我怀疑的性能猪头。

最后,Spring.net以更加面向配置的方式接近这个目标,但是可能值得看看它们的实现 - 这里有个好的reference blog post

-3

而不是做这样,你也可以直接使用一个类型解析的东西,如:

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

最近我在那里有很多传统的Web窗体应用程序的公司开始,所以这看起来是一个真正有趣的方法,并且如果我们想将DI添加到现有的网页,可以提供一种方法,谢谢。

我注意到的一点是,注入方法使用container.Resolve来显式地解析组件,因此我认为我们可能需要做一个容器。在页面卸载时释放组件。

如果我们有瞬态分量,不这样做,那么我们可能会面临内存泄漏。不知道具有Per Web Request生活方式的组件会如何表现(即,尽管我们明确地解决了这些问题,Windsor会在Web请求结束时选择它们),但这里也可能希望保证安全。

因此,模块可能需要进行扩展以跟踪它解决的组件并释放它们,以便Windsor知道何时清理。

3

下面是OP代码的一个修改版本,它(i)缓存注入属性以避免重复反射调用,(ii)释放所有已解析的组件,(iii)封装容器访问以便不公开实现。

// 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() 
    { } 

} 
1

一两件事,从接受的答案,缺少的是(取决于应用程序)之前,该模块将在解决实际问题上的代码依赖的是,HTTP模块需要在web.config文件中注册的事实 - 隐藏页面。你需要的是:

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

除此之外,接受的解决方案像一个魅力工作。

参考微软网站添加HTTP模块:https://msdn.microsoft.com/en-us/library/ms227673.aspx