【问题标题】:ASP.NET Dependency Injection HTTP Module (MS Enterprise Library)ASP.NET 依赖注入 HTTP 模块(MS 企业库)
【发布时间】:2016-02-17 12:03:27
【问题描述】:

我一直按照“Microsoft Enterprise Library 5.0”文档中的步骤创建一个 HTTP 模块,以将 Enterprise Library 容器的引用注入到 ASP.NET Web 应用程序的页面中。

它包含以下代码(也出现在网上here):

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 事件引用了一个“上下文”变量...在此上下文中似乎不存在。

关于我在这里缺少什么的任何想法?

【问题讨论】:

    标签: c# asp.net .net enterprise-library


    【解决方案1】:

    似乎文档组织不当。

    针对(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();
          }
        }
      }
    }
    

    关于依赖注入模块代码,我实际上只是使用basic method 来获取容器的一个实例,就我而言,它也可以正常工作。文档说依赖注入 HTTP 模块代码提高了“可测试性”和“可发现性”,这有点模糊。

    无论如何,这是基本方法的代码:

    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() 扩展方法。

    【讨论】:

    • 糟糕的组织并不能真正削减它 - 代码首先不会编译,并且如果您进行一些基本更改以使其编译,则会出现运行时异常。我已经在我的回答中详细说明了这些内容。
    【解决方案2】:

    MSDN 文章提供的代码实际上非常令人震惊。首先,它不会编译,因为你会在这一行得到一个未声明的变量错误:

    context.PreRequestHandlerExecute -= OnPreRequestHandlerExecute;
    

    因为上下文被传递到 Init 方法中,而不是存储在任何地方。如果您确实捕获该参数并将其存储在一个字段中,那么您将获得运行时异常:

    事件处理程序只能在期间绑定到 HttpApplication 事件 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,但属性注入和方法注入一样工作正常。

    【讨论】:

      猜你喜欢
      • 2011-06-02
      • 2020-01-06
      • 2011-09-22
      • 2015-10-06
      • 2023-03-18
      • 2020-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多