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






4.80/5 (4投票s)
本文讨论了在使用 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 但未能正确处理控制器的生命周期会发生什么。
MEF 创建了 HomeController
类的一个实例。ASP.NET MVC 3 系统发出 HTML 并将其发送到用户的浏览器。用户点击“创建新”链接。该点击会产生一个新的 HTTP 请求。为了显示“创建”页面,ASP.NET MVC 3 系统需要 HomeController
类的一个新实例,但 MEF 尚未生成新的控制器对象。在图 2 中,ASP.NET MVC 3 系统响应用户的点击,反映出这种缺陷。
您可以下载 Mvc3MefLifespanError
解决方案,该方案演示了控制器生命周期管理不当的问题。
在 MEF 中,您至少有两种选择可以解决此问题
- 使用
PartCreationPolicyAttribute
;或 - 在每次页面请求时交替构造和销毁组合容器。
选项 1 — PartCreationPolicyAttribute 的用法
在示例 1 中,我使用 PartCreationPolicyAttribute(CreationPolicy.NonShared)
标记了 HomeController
类。CreationPolicy.NonShared
指示 MEF,每当应用程序需要 HomeController
对象时,都应创建一个 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.BeginRequest
和 HttpApplicantion.EndRequest
事件的方法来利用这一功能。
在示例 2 中,我提供了一个与我在 ExportAttribute、ImportAttribute、CompositionContainer 和 MEF 在 ASP.NET MVC 3 中的应用 文章的示例 12 中展示的实现不同的 IDependencyResolver
接口的实现。我有一个静态的 CompositionContainer
属性,而不是一个在构造函数中初始化的私有变量。
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
实例。
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
。
protected void Application_EndRequest()
{
MefDependencySolver.CompositionContainer.Dispose();
}
构造/销毁组合容器的技术允许您忽略 MEF 部件创建策略,但它要求您在 MvcApplication
类中编写更多代码。该技术还有一个额外的优点,即它可以防止因不小心而很容易发生的内存泄漏。
我已将完整的 Mvc3MefLifespanCompositionContainer 解决方案包含在可下载的包中,以演示构造/销毁技术。
摘要
如果您在 ASP.NET MVC 3 应用程序中使用 MEF,请注意其陷阱。您需要处理控制器的生命周期。本文演示了两种技术。您可以使用 PartCreationPolicyAttribute
,或者在应用程序响应 BeginRequest
和 EndRequest
事件时创建和处理组合容器。创建/处理技术的一个好处是,您还可以避免潜在的内存泄漏。