65.9K
CodeProject 正在变化。 阅读更多。
Home

PartCreationPolicyAttribute、控制器生命周期和 MEF 在 ASP.NET MVC 3 中的应用

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (4投票s)

2011年8月31日

CPOL

5分钟阅读

viewsIcon

20529

downloadIcon

299

本文讨论了在使用 ASP.NET MVC 3 应用程序中的托管可扩展性框架 (MEF) 时,如何处理控制器的生命周期。如果您对 MEF 有一定的理解和知识,本文将对您有所帮助。

引言

本文讨论了在使用 ASP.NET MVC 3 应用程序中的托管可扩展性框架 (MEF) 时,如何处理控制器的生命周期。如果您对 MEF 有一定的理解和知识,并且阅读过 ExportAttribute、ImportAttribute、CompositionContainer 和 MEF 在 ASP.NET MVC 3 中的应用,那么本文将对您有所裨益。本文不涉及 MEF 或 ASP.NET MVC 3 系统的复杂细节。

故事配有可下载的 Visual Studio 解决方案

  • 名为 Mvc3MefLifespanError 的解决方案演示了通过 MEF 实例化控制器时的一个常见错误。
  • 名为 Mvc3MefLifespanPartCreationPolicy 的解决方案展示了创建策略属性的使用。
  • 名为 Mvc3MefLifespanCompositionContainer 的解决方案展示了组合容器的构造/销毁技术。

截至 2011 年 8 月,您应考虑以下软件

  • Visual Studio 2010
  • Visual Studio 2010 SP1
  • .NET Framework 4.0
  • ASP.NET MVC 3
  • C# 4.0。

如果您运行一个标准的 ASP.NET MVC 3 应用程序,每当收到一个 HTTP 请求(例如,用户点击屏幕上的某个链接)时,ASP.NET MVC 3 系统都会创建一个指定控制器类的新实例。当开发者在应用程序中使用 MEF 时,这一事实常常会让一些未经充分准备的开发者感到困惑。这种缺乏远见的做法会以一条令人不快的消息“一个控制器的实例‘xxx’不能用于处理多个请求”显现出来。

图 1 和图 2 说明了如果您使用 MEF 但未能正确处理控制器的生命周期会发生什么。

图 1 — 使用 MEF 的 ASP.NET MVC 3 应用程序。用户已请求 Index 页面。

figure-1.png

MEF 创建了 HomeController 类的一个实例。ASP.NET MVC 3 系统发出 HTML 并将其发送到用户的浏览器。用户点击“创建新”链接。该点击会产生一个新的 HTTP 请求。为了显示“创建”页面,ASP.NET MVC 3 系统需要 HomeController 类的一个新实例,但 MEF 尚未生成新的控制器对象。在图 2 中,ASP.NET MVC 3 系统响应用户的点击,反映出这种缺陷。

图 2 — 使用 MEF 的 ASP.NET MVC 3 应用程序。响应用户点击的错误消息。

figure-2.png

您可以下载 Mvc3MefLifespanError 解决方案,该方案演示了控制器生命周期管理不当的问题。

在 MEF 中,您至少有两种选择可以解决此问题

  • 使用 PartCreationPolicyAttribute;或
  • 在每次页面请求时交替构造和销毁组合容器。

选项 1 — PartCreationPolicyAttribute 的用法

在示例 1 中,我使用 PartCreationPolicyAttribute(CreationPolicy.NonShared) 标记了 HomeController 类。CreationPolicy.NonShared 指示 MEF,每当应用程序需要 HomeController 对象时,都应创建一个 HomeController 类的新实例。

示例 1 — PartCreationPolicyAttribute 装饰了 HomeController 类。
[
  ExportAttribute
  ,
  PartCreationPolicyAttribute(CreationPolicy.NonShared)
]
public class HomeController : Controller
{
  [ImportingConstructorAttribute]
  public HomeController(MessageSource messageSource)
  {
    this.messageSource = messageSource;
  }
  .
  .
  .
}

完整的 Visual Studio Mvc3MefLifespanPartCreationPolicy 解决方案演示了创建策略属性。

MEF 开发者普遍认为,默认情况下,组合容器会创建(用微软的说法)单个部分或单例对象。事实是,默认的创建策略是 CreationPolicy.Any。然而,CreationPolicy.Any 有一个诀窍。如果导入方不指定其偏好,组合容器就会生成一个对象。

这解释了为什么我在运行 Mvc3MefLifespanError 示例解决方案时会收到错误(参见图 2)。由于我没有指定创建策略,因此默认策略是 CreationPolicy.Any。但是,在没有导入方的情况下,组合容器会为每个 HTTP 页面请求创建一个 HomeController 类实例,并将其提供给 ASP.NET MVC 3 系统。

选项 2 — 在每次页面请求时构造和销毁组合容器

当 ASP.NET MVC 3 系统处理 HTTP 请求时,它会请求您的自定义依赖解析器从您的 MEF 组合容器中获取对象。当您从容器中获取对象时,容器会满足所有具有相关导出的导入。

我通过提供响应 HttpApplicantion.BeginRequestHttpApplicantion.EndRequest 事件的方法来利用这一功能。

在示例 2 中,我提供了一个与我在 ExportAttribute、ImportAttribute、CompositionContainer 和 MEF 在 ASP.NET MVC 3 中的应用 文章的示例 12 中展示的实现不同的 IDependencyResolver 接口的实现。我有一个静态的 CompositionContainer 属性,而不是一个在构造函数中初始化的私有变量。

示例 2 — 带有静态 CompositionContainer 属性的 MefDependencySolver 类。
namespace MefLifespanMvc03
{
  using System;
  using System.Collections.Generic;
  using System.ComponentModel.Composition;
  using System.ComponentModel.Composition.Hosting;
  using System.Web.Mvc;

  public class MefDependencySolver : IDependencyResolver
  {
    public static CompositionContainer CompositionContainer { get; set; }


    public object GetService(Type serviceType)
    {
      string name = AttributedModelServices.GetContractName(serviceType);
      return CompositionContainer.GetExportedValueOrDefault<object>(name);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
      return CompositionContainer.GetExportedValues<object>(serviceType.FullName);
    }
  }
}

在示例 3 中,我在 MvcApplication 类中定义了 Application_BeginRequest 方法。当 HTTP 请求启动时,ASP.NET MVC 3 系统会调用该方法,将我的自定义依赖解析器的 CompositionContainer 属性设置为新创建的 CompositionContainer 实例。

示例 3 — 创建 CompositionContainer 的新实例。
protected void Application_BeginRequest()
{
  TypeCatalog typeCatalog = new TypeCatalog(typeof(HomeController),
                                            typeof(MessageSource));
  MefDependencySolver.CompositionContainer = 
    new CompositionContainer(typeCatalog);
}

在示例 4 中,我在 MvcApplication 类中定义了 Application_EndRequest 方法。当 HTTP 请求完成时,ASP.NET MVC 3 系统会调用该方法,销毁我的自定义依赖解析器的 CompositionContainer

示例 4 — 销毁 CompositionContainer。
protected void Application_EndRequest()
{
  MefDependencySolver.CompositionContainer.Dispose();
}

构造/销毁组合容器的技术允许您忽略 MEF 部件创建策略,但它要求您在 MvcApplication 类中编写更多代码。该技术还有一个额外的优点,即它可以防止因不小心而很容易发生的内存泄漏。

我已将完整的 Mvc3MefLifespanCompositionContainer 解决方案包含在可下载的包中,以演示构造/销毁技术。

摘要

如果您在 ASP.NET MVC 3 应用程序中使用 MEF,请注意其陷阱。您需要处理控制器的生命周期。本文演示了两种技术。您可以使用 PartCreationPolicyAttribute,或者在应用程序响应 BeginRequestEndRequest 事件时创建和处理组合容器。创建/处理技术的一个好处是,您还可以避免潜在的内存泄漏。

© . All rights reserved.