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

表示模型和依赖注入

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (10投票s)

2009年3月17日

Ms-PL

5分钟阅读

viewsIcon

47473

downloadIcon

445

ASP.NET MVVM 提供了一个实现 Presentation Model 模式的框架,也称为 ASP.NET 项目中的 Model-View-ViewModel 模式。开发人员可以利用依赖注入和事件代理来实现简洁、优雅且专注于业务的代码。

pm_on_azure.png

引言

Presentation Model 是一种将内容与表示以及数据处理(模型)与内容分离的设计模式。在我另一篇题为《Presentation Model in Action》的文章中,我演示了如何在网站、Windows 窗体应用程序和 WPF 应用程序中使用 Presentation Model 模式。

Model-View-ViewModel (MVVM, M-V-VM) 模式是 Presentation Model 模式的另一个名称。在最近发布的 MSDN 杂志中,有两篇文章介绍了在 WPF(2009 年 2 月)Silverlight(2009 年 3 月)中使用 MVVM 模式。也许在下一期 MSDN 杂志中,他们会发布另一篇关于在 ASP.NET 中使用 MVVM 的文章。

同时,我已经将依赖注入和事件代理引入到该模式中,并创建了一个名为 ASP.NET MVVM Framework on CodePlex 的新项目。在本文的后续部分,我将继续使用 Presentation Model 模式这个术语来描述依赖注入和事件代理如何在 ASP.NET 平台上为该模式发挥作用。

依赖注入

Presentation Model 模式中,有一个集中的地方用于存储状态/数据。它也是一个集中的事件源,与视图完全独立。

ASPNET_MVVM_Solution_-_1

上面来自 MSDN 的图表显示了对象之间的关系是

  • View 引用 Presentation Model
  • View 订阅 Presentation Model 的事件
  • Presentation Model 引用 Model
  • Presentation Model 订阅 Model 的事件

当该模式在 ASP.NET 平台上使用时

  • View 通过接口引用 Presentation Model,以实现解耦和可测试性
  • Presentation Model 实现许多 View 所需的接口
  • Presentation Model 实例由多个 View 共享。例如,在 Web 窗体 (ASPX) 上创建的 Presentation Model 实例由该 Web 窗体上的所有用户控件 (ASCX) 共享

View 只知道一个接口,而不是 Presentation Mode 的类型。View 不知道如何创建 Presentation Model 实例。此外,Presentation Model 实例在多个 View 之间共享。没有任何 View 拥有 Presentation Model 实例。

为了管理这里的对象,控制反转原则就派上用场了。它带来了另外两个模式:服务定位器模式依赖注入模式

如果使用服务定位器模式,则有一个 Service Locator 类,它知道如何创建 Presentation Model 实例。该类还包含对 Presentation Model 实例的引用。View 调用 Service Locator 类来获取 Presentation Model 实例的引用。

public ICustomerList controller {get; set;}
protected void Page_Load(object sender, EventArgs e)
{
    controller = ControllerLocator.GetController(this) as ICustomerList;
    customerGrid.DataSource = controller.Customers;
}

如果使用依赖注入模式,它的工作方式就像好莱坞原则一样:“不要给我们打电话,我们会给你打电话”。View 甚至不知道助手在哪里。View 只标记需要 Presentation Model 实例的位置。在 View 创建后,Presentation Model 实例就会在那里 ready for use。这通过 ASP.NET MVVM Framework 中的自定义 HttpModule 来实现。

[Inject]
public ICustomerList controller {get; set;}
protected void Page_Load(object sender, EventArgs e)
{
    customerGrid.DataSource = controller.Customers;
}

有许多框架提供依赖注入功能,例如 Unity。ASP.NET MVVM Framework 与其他框架的主要区别在于,ASP.NET MVVM Framework 不创建特殊的容器。Presentation Model 实例默认存储在控件树中。在查找 Presentation Model 实例时,ASP.NET MVVM Framework 会查找控件树以找到它们。它会搜索用户控件的父级,然后是父级的父级,直到找不到父级为止。支持嵌套的用户控件层次结构。这种灵活性使我们能够继续使用用户控件作为应用程序构建块。

Presentation Model 实例可以存储在 ASP.NET 的内置对象存储中:Application、Session 和 Request。

[Inject(Scope=InjectScope.Application)]
public ILogger logger {get; set;}

[Create(Scope=CreationScope.Session)]
public CustomerController controller {get; set;}

protected void Page_Load(object sender, EventArgs e)
{
}

事件代理

在 Presentation Model 模式中,Presentation Model 发布事件,View 订阅事件。事件代理模式提供了一种优雅的方法来管理这种发布和订阅关系。

Microsoft Composite UI Application Block 中,有一个事件代理系统,它允许在组件之间发布和订阅事件。事件发布者按如下方式发布事件

[EventPublication("event://UpdatesAvailable", PublicationScope.Global)]
public event SomeEventHandler UpdatesAvailable;

事件消费者按如下方式订阅事件

[EventSubscription("event://UpdatesAvailable")]
public void NewUpdates(object sender, SomeEventArgs numUpdates)
{
}

ASP.NET MVVM 将此模式引入 ASP.NET 并进行了简化

  1. Presentation Model 中的所有事件都会被发布。
    ASP.NET MVVM 假定 Presentation Model 中的所有 public 事件都会被发布。不需要 Event Publication Attribute。通过这样做,Presentation Model 与 ASP.NET MVVM Framework 没有依赖关系。我们应该能够重用 Presentation Model,而无需对 WPF 和 Silverlight 等其他应用程序平台进行任何更改。
  2. 事件名称即事件主题。
    ASP.NET MVVM Framework 默认通过事件名称和函数名称匹配发布和订阅。它还可以通过提供事件名称和 Presentation Model 实例的属性名称来匹配。例如,下面的事件和函数将匹配
// The event in the Presentation Model / ViewModel
public event EventHandler CustomerChanged;

// In the View, subscribe to event with same name 
[EventSubscription]
private void CustomerChanged(object sender, EventArgs e)
{
}

// In the View, subscribe to event that matches a name 
[EventSubscription(EventName= “CustomerChanged”)]
private void controller_CustomerChanged(object sender, EventArgs e)
{
}

// In the View, subscribe to event that matches a name 
// and the Presentation Model property name 
[EventSubscription(EventName=“CustomerChanged”, ControllerName=”controller”)]
private void controller_CustomerChanged(object sender, EventArgs e)
{
}

在 ASP.NET 中,如果在页面加载时订阅了某些事件,则必须在页面卸载时取消订阅。否则,如果事件发布者位于 Session 或 Application 中,它将持有 View 中事件处理程序的引用。然后 View 将不会被垃圾回收,这可能导致内存泄漏。

[Inject]
public ICustomerList controller { get; set;}
protected void Page_Load(object sender, EventArgs e)
{
    controller.CustomerListChanged += new EventHandler(CustomerListChanged);
}

protected void Page_Unload(object sender, EventArgs e)
{
    controller.CustomerListChanged -= new EventHandler(CustomerListChanged);
}

为防止内存泄漏,ASP.NET MVVM Framework 会存储事件和订阅方法链接。当 Web 页面卸载时,ASP.NET MVVM Framework 使用存储的链接取消订阅事件。这是在后台完成的。View 中不需要取消订阅代码。

使用代码

上面讨论的依赖注入和事件代理实现了一个轻量级框架,您可以通过本文顶部的链接下载。它专为 ASP.NET 平台设计。要使用它,请添加对项目的引用,并在 web.config<httpModules> 部分中添加 HttpModule

<add name="PageControllerModule" 
	type="Demo.Web.Mvvm.PageControllerModule, Demo.Web.Mvvm"/>

这就够了。现在您可以开始享受 [Create][Inject][EventSubscription] 属性了。

示例应用程序来自我之前的文章,但使用属性进行了重写。该应用程序不仅可以在普通的 Windows 服务器上运行,还可以在 Windows Azure 平台上运行。我在 Windows Azure 上对其进行了测试。此页面顶部的屏幕截图显示了在 Windows Azure 上运行的示例应用程序。

结论

ASP.NET MVVM 旨在实现 Model-View-ViewModel 模式,也称为 ASP.NET 项目中的 Presentation Model 模式。开发人员可以利用依赖注入和事件代理来实现简洁、优雅且专注于业务的代码。它提供了以下优势:

  • 利用 ASP.NET 功能,如 Web 窗体、用户控件和数据绑定
  • 实现关注点分离
  • 促进测试驱动开发 (TDD)
  • 使用属性注解编写和维护更简单的代码
© . All rights reserved.