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"变量。。。在这种情况下似乎不存在。

你知道我在这里遗漏了什么吗?

ASP.NET依赖项注入HTTP模块(MS企业库)

似乎文档组织得不好。

对于(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,但属性注入和方法注入都很好。