自定義IHttpHandlerFactory使用Unity對ASP.NET Webform頁面進行依賴注入

 簡介:使用HttpHandlerFactory對ASP.NET Webform的頁面進行依賴注入,不僅僅是Unity,使用同樣的思路也可以用Spring.NET

 

背景

在日常的開發中,特別是使用了多層結構的程序,在視圖層的頁面邏輯中時常會用到業務邏輯的對象,此時就有可能產生如下的代碼

Unity&WebForm(1): 自定義IHttpHandlerFactory使用Unity對ASP.NET Webform頁面進行依賴注入public partial class Default : System.Web.UI.Page
Unity&WebForm(1): 自定義IHttpHandlerFactory使用Unity對ASP.NET Webform頁面進行依賴注入
{
Unity&WebForm(1): 自定義IHttpHandlerFactory使用Unity對ASP.NET Webform頁面進行依賴注入    
public IUser UserService
Unity&WebForm(1): 自定義IHttpHandlerFactory使用Unity對ASP.NET Webform頁面進行依賴注入    
{
Unity&WebForm(1): 自定義IHttpHandlerFactory使用Unity對ASP.NET Webform頁面進行依賴注入        
get;
Unity&WebForm(1): 自定義IHttpHandlerFactory使用Unity對ASP.NET Webform頁面進行依賴注入        
set;
Unity&WebForm(1): 自定義IHttpHandlerFactory使用Unity對ASP.NET Webform頁面進行依賴注入    }

Unity&WebForm(1): 自定義IHttpHandlerFactory使用Unity對ASP.NET Webform頁面進行依賴注入
Unity&WebForm(1): 自定義IHttpHandlerFactory使用Unity對ASP.NET Webform頁面進行依賴注入    
protected void Page_Load(object sender, EventArgs e)
Unity&WebForm(1): 自定義IHttpHandlerFactory使用Unity對ASP.NET Webform頁面進行依賴注入    
{
Unity&WebForm(1): 自定義IHttpHandlerFactory使用Unity對ASP.NET Webform頁面進行依賴注入       
Unity&WebForm(1): 自定義IHttpHandlerFactory使用Unity對ASP.NET Webform頁面進行依賴注入    }

Unity&WebForm(1): 自定義IHttpHandlerFactory使用Unity對ASP.NET Webform頁面進行依賴注入}


此代碼的問題在於,沒有一個適當的地方初始化User對象,雖然可以藉助工廠在Page_Load中加入初始化的邏輯,但是每一個頁面都需要手動地初始化對象顯然並不合適
而在MVC模式下,在Controller中經常會用到依賴注入的方式將User對象注入,例如JAVA中的Structs和Webworks都可以配合Spring進行注入,而新出的ASP.NET MVC Framework也有相關的配合Unity進行依賴注入的技術文章
但是對於Webform,因爲整個頁面的執行相對封閉,沒有很好的擴展環節,使注入顯得不是那麼容易
此文將使用自定義的IHttpHandlerFactory,在頁面的生成時期使用Unity進行依賴注入,在不影響系統的運行的前提下,無侵入性地完成此項工作

 

原理

在ASP.NET的執行週期中,在經過了HttpModule之後,會由HttpHandlerFactory生成具體的Handler,隨後進行Handler的生命週期,因此如果需要對同是HttpHandler的Page進行依賴注入,就需要使用HttpHandlerFactory的GetHandler方法生成完成注入後的Page對象
原本ASP.NET中用於生成Page對象的HttpHandlerFactory叫PageHandlerFactory,此類的構造函數被隱藏了,因此需要使用Activator來生成對象,再從PageHandlerFactory的對象中生成原有的Page對象
在獲取了Page對象之後,我們就可以使用Unity爲其進行依賴注入,所幸的是Unity提供了BuildUp方法來對已經生成的對象進行注入
當然其間還是有不少需要注意的問題,在實現環節中一一說明

 

using System;
using System.Web;
using System.Web.UI;
using System.Configuration;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;

namespace MyDataUtilyTest
{
    //此類實現原理

    /// <summary>
    /// 在ASP.NET的執行週期中,在經過了HttpModule之後,會由HttpHandlerFactory生成具體的Handler,
    /// 隨後進行Handler的生命週期,因此如果需要對同是HttpHandler的Page進行依賴注入,
    /// 就需要使用HttpHandlerFactory的GetHandler方法生成完成注入後的Page對象
    /// 原本ASP.NET中用於生成Page對象的HttpHandlerFactory叫PageHandlerFactory,
    /// 此類的構造函數被隱藏了,因此需要使用Activator來生成對象,
    /// 再從PageHandlerFactory的對象中生成原有的Page對象
    /// 在獲取了Page對象之後,我們就可以使用Unity爲其進行依賴注入,
    /// 所幸的是Unity提供了BuildUp方法來對已經生成的對象進行注入
    /// </summary>
   
    public class UnityHttpHandlerFactory : IHttpHandlerFactory
    {
        //需要初始化一個UnityContainer,這裏使用靜態變量
        private static readonly IUnityContainer unityContainer;

        static UnityHttpHandlerFactory()
        {
            //初始化UnityContainer
            string containerName = ConfigurationManager.AppSettings["HttpHandlerUnityContainerName"];
            if (String.IsNullOrEmpty(containerName))
            {
                containerName = "HttpHandlerContainer";
            }

            unityContainer = new UnityContainer();
            UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
            section.Containers[containerName].Configure(unityContainer);
        }

        public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
        {
            IHttpHandlerFactory pageFactory = CreatePageFactory();
            IHttpHandler pageHandle = pageFactory.GetHandler(context,requestType,url,pathTranslated);
            pageHandle = Build(pageHandle);
            return pageHandle;
        }

        public void ReleaseHandler(IHttpHandler handler)
        {
            IHttpHandlerFactory pageFactory = CreatePageFactory();
            pageFactory.ReleaseHandler(handler);
        }

        /// <summary>
        /// 在GetHandler方法中,取得具體的Page對象後,使用了一個Build方法對其進行包裝,
        /// </summary>
        /// <param name="httpHandler"></param>
        /// <returns></returns>
        private static IHttpHandler Build(IHttpHandler httpHandler)
        {
            //Build方法使用UnityContainer的BuildUp方法對Page對象進行注入,這裏有2點需要注意
            //注意1.因爲並不能保證每一個Page都在UnityContainer中有註冊,所以此處的BuildUp並不保證成功,
            //需要用catch捕獲BuildUp過程中拋出的異常,如果BuildUp失敗,則返回原有的實例即可
            //注意2.在ASP.NET運行期間,PageHandlerFactory生成的是編程時相應Page的子類,所以在
            //UnityContainer中尋找註冊的類型的時候,需要使用page.GetType().BaseType纔可
            //

            try
            {
                return unityContainer.BuildUp(httpHandler.GetType().BaseType,httpHandler) as IHttpHandler;
            }
            catch
            {
                return httpHandler;
            }
        }

        private static IHttpHandlerFactory CreatePageFactory()
        {
            //簡單地使用Activator構造一個PageHandlerFactory的實例
            //在這裏CreateInstance方法給了兩個參數,第二個參數是這樣子的,
            //如果公共或非公共默認構造函數可以匹配,則爲 true;如果只有公共默認構造函數可以匹配,則爲 false。


            IHttpHandlerFactory pageFactory =
                Activator.CreateInstance(typeof(PageHandlerFactory), true) as IHttpHandlerFactory;
            if (pageFactory == null)
            {
                throw new ApplicationException("Unable to initialize 'PageHandlerFactory'");
            }

            return pageFactory;
        }

       

    }
   
}

 

 

爲了保持靈活性,使用了配置文件的方式讀取UnityContainer,默認配置在web.config中

 

最後的工作當然是將此HttpHandlerFactory加入運行環境,在web.config中system.web配置組下的httpHandlers段加入以下2行即可

Unity&WebForm(1): 自定義IHttpHandlerFactory使用Unity對ASP.NET Webform頁面進行依賴注入<remove verb="*" path="*.aspx"/>
Unity&WebForm(1): 自定義IHttpHandlerFactory使用Unity對ASP.NET Webform頁面進行依賴注入
<add verb="*" path="*.aspx" type="Cst.Core.Web.Factory.UnityHttpHandlerFactory, Cst.Core.Web"/>

這2行將原有的默認HttpHandlerFactory(即PageHandlerFactory)取消,代替以新的UnityHttpHandlerFactory

 

問題

1.因爲在Unity中註冊的頁面其實只是用來作爲BuildUp的參考,所以其生命週期管理是Transient好還是Singleton好依舊是個問題,有待更詳細的測試
2.Unity只提供對public的屬性的注入,需要對protected屬性注入還要自己寫擴展
3.只能用屬性注入,因爲Page的構造是由PageHandlerFactory完成的,Unity無力攔截構造函數的注入

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章