您的位置:首页 > 理论基础 > 计算机网络

自定义IHttpHandlerFactory使用Unity对ASP.NET Webform页面进行依赖注入

2009-10-29 17:09 645 查看
简介:使用HttpHandlerFactory对ASP.NET Webform的页面进行依赖注入,不仅仅是Unity,使用同样的思路也可以用Spring.NET

背景

在日常的开发中,特别是使用了多层结构的程序,在视图层的页面逻辑中时常会用到业务逻辑的对象,此时就有可能产生如下的代码


public partial class Default : System.Web.UI.Page


{


public IUser UserService


{


get;


set;


}




protected void Page_Load(object sender, EventArgs e)


{




}


}

此代码的问题在于,没有一个适当的地方初始化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行即可


<remove verb="*" path="*.aspx"/>


<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无力拦截构造函数的注入
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: