ASP.NET依赖项注入HTTP模块(MS企业库)
本文关键字:MS 企业库 模块 HTTP NET 依赖 注入 ASP | 更新日期: 2024-10-23 05:29:27
我一直在按照"Microsoft Enterprise Library 5.0"文档中的步骤创建一个HTTP模块,以便将对Enterprise Library容器的引用注入ASP.NET web应用程序的页面中。
它包含以下代码(也在此处在线显示):
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using Microsoft.Practices.Unity;
namespace Unity.Web
{
public class UnityHttpModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += OnPreRequestHandlerExecute;
}
public void Dispose() { }
private void OnPreRequestHandlerExecute(object sender, EventArgs e)
{
IHttpHandler currentHandler = HttpContext.Current.Handler;
HttpContext.Current.Application.GetContainer().BuildUp(
currentHandler.GetType(), currentHandler);
// User Controls are ready to be built up after page initialization is complete
var currentPage = HttpContext.Current.Handler as Page;
if (currentPage != null)
{
currentPage.InitComplete += OnPageInitComplete;
}
}
// Build up each control in the page's control tree
private void OnPageInitComplete(object sender, EventArgs e)
{
var currentPage = (Page)sender;
IUnityContainer container = HttpContext.Current.Application.GetContainer();
foreach (Control c in GetControlTree(currentPage))
{
container.BuildUp(c.GetType(), c);
}
context.PreRequestHandlerExecute -= OnPreRequestHandlerExecute;
}
// Get the controls in the page's control tree excluding the page itself
private IEnumerable<Control> GetControlTree(Control root)
{
foreach (Control child in root.Controls)
{
yield return child;
foreach (Control c in GetControlTree(child))
{
yield return c;
}
}
}
}
}
这个代码和它附带的指令有很多问题。
1) 说明中没有提到将此代码放置在何处。由于它是一个类,我将它放在ASP.NET网站项目的App_Code文件夹中。
事实上,以下是这段代码的说明:
创建一个新的ASP.NET HTTP模块类(例如,UnityHttpModule)PreRequestHandlerExecute事件并执行代码当前页面请求的完整控制树,应用Unity每个控件的BuildUp方法。
2) HttpContext.Current.Application.GetContainer()方法对我来说不存在,尽管我使用了相同的DLL引用(我在.NET 4.0中编码)。
3) OnPageInitComplete事件引用了"context"变量。。。在这种情况下似乎不存在。
你知道我在这里遗漏了什么吗?
似乎文档组织得不好。
对于(2),没有解释的是HttpContext.Current.Application.GetContainer()方法实际上是一个扩展方法,其实现方式与here
所示的代码类似。
要使用此扩展方法,只需导入"Unity.Web"命名空间即可。
以下是扩展方法的副本:
using System.Web;
using Microsoft.Practices.Unity;
namespace Unity.Web
{
public static class HttpApplicationStateExtensions
{
private const string GlobalContainerKey = "EntLibContainer";
public static IUnityContainer GetContainer(this HttpApplicationState appState)
{
appState.Lock();
try
{
var myContainer = appState[GlobalContainerKey] as IUnityContainer;
if (myContainer == null)
{
myContainer = new UnityContainer();
appState[GlobalContainerKey] = myContainer;
}
return myContainer;
}
finally
{
appState.UnLock();
}
}
}
}
关于依赖注入模块代码,我实际上只是使用了获取容器实例的基本方法,就我而言,它也同样有效。文档中说,依赖注入HTTP模块代码提高了"可测试性"answers"可发现性",这有点模糊。
总之,以下是基本方法的代码:
protected void Application_Start(object sender, EventArgs e)
{
Application.Lock();
try
{
var myContainer = Application["EntLibContainer"] as IUnityContainer;
if (myContainer == null)
{
myContainer = new UnityContainer();
myContainer.AddExtension(new EnterpriseLibraryCoreExtension());
// Add your own custom registrations and mappings here as required
Application["EntLibContainer"] = myContainer;
}
}
finally
{
Application.UnLock();
}
}
因此,有了扩展代码,以及我的global.asax文件中的代码来创建企业库容器的实例,剩下的唯一事情就是根据需要编写代码来获得容器的实例。因此,当我想获得LogWriter类的实例时,我会写下:
using Unity.Web;
public LogWriter getLogWriter()
{
var container = HttpContext.Current.Application.GetContainer();
return container.Resolve<LogWriter>();
}
需要Unity.Web命名空间才能调用GetContainer()扩展方法。
MSDN文章提供的代码实际上非常令人震惊。首先,它不会编译,因为你会在这一行得到一个未声明的变量错误:
context.PreRequestHandlerExecute -= OnPreRequestHandlerExecute;
As上下文被传递到Init方法中,而不是存储在任何地方。如果您确实捕获了该参数并将其存储在字段中,那么您将得到一个运行时异常:
事件处理程序只能在IHttpModule初始化。
因此,以下内容似乎有效:
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using Microsoft.Practices.Unity;
namespace Unity.Web
{
/// <summary>
/// An <see cref="IHttpModule" /> that automatically injects dependencies into ASP.NET WebForms pages.
/// </summary>
/// <remarks>
/// Since the pages have already been constructed by the time the module is called, constructor injection cannot be used. However,
/// property injection can be used instead.
/// </remarks>
public class UnityHttpModule : IHttpModule
{
private HttpApplication _context;
private bool _disposed;
/// <summary>
/// Initializes a module and prepares it to handle requests.
/// </summary>
/// <param name="context">An <see cref="T:System.Web.HttpApplication"/> that provides access to the methods, properties, and events common to all application objects within an ASP.NET application </param>
public void Init(HttpApplication context)
{
_context = context;
_context.PreRequestHandlerExecute += OnPreRequestHandlerExecute;
}
/// <summary>
/// Disposes of the resources (other than memory) used by the module that implements <see cref="T:System.Web.IHttpModule"/>.
/// </summary>
public void Dispose()
{
GC.SuppressFinalize(this);
Dispose(true);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
if (_context != null)
{
_context.PreRequestHandlerExecute -= OnPreRequestHandlerExecute;
}
}
_disposed = true;
}
/// <summary>
/// Handles the <see cref="E:PreRequestHandlerExecute" /> event.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="eventArgs">The <see cref="EventArgs"/> instance containing the event data.</param>
private void OnPreRequestHandlerExecute(object sender, EventArgs eventArgs)
{
var currentHandler = HttpContext.Current.Handler;
if (currentHandler != null)
{
HttpContext.Current.Application.GetContainer().BuildUp(currentHandler.GetType(), currentHandler);
}
// User Controls are ready to be built up after page initialization is complete
var currentPage = HttpContext.Current.Handler as Page;
if (currentPage != null)
{
currentPage.InitComplete += OnPageInitComplete;
}
}
/// <summary>
/// Handles the <see cref="E:PageInitComplete" /> event.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
private void OnPageInitComplete(object sender, EventArgs e)
{
var currentPage = (Page)sender;
var container = HttpContext.Current.Application.GetContainer();
foreach (var c in GetControlTree(currentPage))
{
container.BuildUp(c.GetType(), c);
}
}
/// <summary>
/// Gets the controls in the page's control tree, excluding the page itself.
/// </summary>
/// <param name="root">The root control.</param>
/// <returns>The child controls of the <paramref name="root" /> control.</returns>
private static IEnumerable<Control> GetControlTree(Control root)
{
foreach (Control child in root.Controls)
{
yield return child;
foreach (var control in GetControlTree(child))
{
yield return control;
}
}
}
}
你需要@CiaranGallagher在他的回答中提到的其余基础设施代码来完成管道,尽管我更喜欢使用项目注入器,所以在他的例子中,代码是:
using Unity.Web;
[Dependency]
public LogWriter Writer { get; set; }
您不能将构造函数注入与WebForms一起使用,因为模块在现有控件上使用BuildUp,但属性注入和方法注入都很好。