2008-11-16 52 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