ASP.NETdependency injectionHTTP模块(MS企业库)
我一直在按照“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 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(); } } } }
关于dependency injection模块代码,我实际上只是使用基本方法来获取容器的实例,就我而言,同样适用于容器的实例。 文档说dependency injectionHTTP模块代码提高了“可测试性”和“可发现性”,这有点模糊。
无论如何,这是基本方法的代码:
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文件中的代码来创建Enterprise Library容器的实例,唯一要做的就是编写代码以根据需要获取容器的实例。 所以,当我想获得LogWriter类的实例时,我会写这个:
using Unity.Web; public LogWriter getLogWriter() { var container = HttpContext.Current.Application.GetContainer(); return container.Resolve(); }
需要Unity.Web命名空间才能允许我们调用GetContainer()扩展方法。
MSDN文章提供的代码实际上非常令人震惊。 首先,它不会编译,因为你会在这一行得到一个未声明的变量错误:
context.PreRequestHandlerExecute -= OnPreRequestHandlerExecute;
因为上下文被传递到Init方法而不存储在任何地方。 如果您捕获该参数并将其存储在字段中,那么您将获得运行时exception:
事件处理程序只能在IHttpModule初始化期间绑定到HttpApplication事件。
因此,以下似乎有效:
using System; using System.Collections.Generic; using System.Web; using System.Web.UI; using Microsoft.Practices.Unity; namespace Unity.Web { /// /// An that automatically injects dependencies into ASP.NET WebForms pages. /// /// /// 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. /// public class UnityHttpModule : IHttpModule { private HttpApplication _context; private bool _disposed; /// /// Initializes a module and prepares it to handle requests. /// /// An that provides access to the methods, properties, and events common to all application objects within an ASP.NET application public void Init(HttpApplication context) { _context = context; _context.PreRequestHandlerExecute += OnPreRequestHandlerExecute; } /// /// Disposes of the resources (other than memory) used by the module that implements . /// public void Dispose() { GC.SuppressFinalize(this); Dispose(true); } /// /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_disposed) { return; } if (disposing) { if (_context != null) { _context.PreRequestHandlerExecute -= OnPreRequestHandlerExecute; } } _disposed = true; } /// /// Handles the event. /// /// The sender. /// The instance containing the event data. 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; } } /// /// Handles the event. /// /// The sender. /// The instance containing the event data. 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); } } /// /// Gets the controls in the page's control tree, excluding the page itself. /// /// The root control. /// The child controls of the control. private static IEnumerable 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,但属性注入工作正常,方法注入也是如此。