WPF客户端与WCF通信及NHibernate持久化的MVVM模式实现

本文将介绍如何在WPF客户端应用程序中使用Windows Communication Foundation (WCF) 进行通信,并使用NHibernate作为数据持久化工具。同时,将采用Model-View-ViewModel (MVVM) 设计模式来构建应用程序的架构。

RelayCommand模式

RelayCommand模式基于Josh Smith关于MVVM模式的文章。将重点关注在解决方案中的实现,而不是详细解释模式本身。在本章的源代码可以在CodePlex变更集93453中找到。eDirectory解决方案的最新代码可以在CodePlex上找到。

ICommand接口

在客户端中,在第六章定义了两个命令按钮,即保存和刷新按钮。当时可能不明显,但在XAML中声明了对ViewModel中动作的绑定。这些方法在第六章的CustomerViewModel中没有定义,但这并没有阻止执行应用程序。值得注意的是XAML视图的这一方面,错误的绑定不会抛出异常;然而,在调试应用程序时,VS中可以找到良好记录的警告。例如,第六章中保存按钮的XAML声明如下:

命令操作将调用ViewModel中的一个只读属性SaveCommand;ViewModel中的属性必须实现ICommand接口才能工作。

RelayCommand类

RelayCommand类提供了一个简单的机制,用于在ViewModel中声明属性,以便按钮可以使用绑定定义调用操作。在保存按钮的例子中,绑定指示按钮将调用一个名为SaveCommand的属性;CustomerViewModel类声明属性如下:

private RelayCommand SaveCommandInstance; public RelayCommand SaveCommand { get { if (SaveCommandInstance != null) return SaveCommandInstance; SaveCommandInstance = new RelayCommand(a => Save()); return SaveCommandInstance; } } private void Save() { var result = CustomerServiceAdapter.Execute(s => s.CreateNewCustomer(Model.NewCustomerOperation)); Refresh(); }

使用延迟实例化RelayCommand私有实例的方法(第2行),这种方法允许使用一个lambda表达式,指示当视图调用RelayCommand的Execute方法时将被调用的内部方法。值得注意的是,RelayCommand有两个构造函数可用;在这个例子中,使用的是只需要传递要调用的操作的构造函数。还有一个重载的构造函数提供了验证机制,在命令按钮的情况下,确保只有在验证成功时按钮才被启用。将在后续章节中看到如何使用这个特性的例子。

服务适配器在ViewModel中的使用

在第3行中,可以看到Save方法正在调用CreateNewCustomer服务方法。需要解释的两个方面是CustomerServiceInstance的声明方式,以及使用NewCustomerOperation DTO将客户详细信息从UI传递到服务的机制。

private readonly ServiceAdapter CustomerServiceAdapter; public CustomerViewModel() { CustomerServiceAdapter = new ServiceAdapter(); Refresh(); View = new CustomerView { DataContext = this }; View.ShowDialog(); }

在第1行中,实例化了ServiceAdapter,表明想要处理这个ViewModel的Customer服务。这是在客户端利用WCF服务的一个非常好的方式;ViewModel没有与WCF服务实现耦合,这在开发和测试时减少了复杂性。它还提供了一个简单的方法来避免使用MS WCF代理,如果正在开发服务器和客户端。在第十二章 - WCF实现中讨论了如何使用这种设计在客户端实现WCF服务是多么容易。

刷新方法

刷新方法在显示视图之前被调用;这样做是为了初始化模型:

private void Refresh() { var result = CustomerServiceAdapter.Execute(s => s.FindAll()); Model = new CustomerModel { NewCustomerOperation = new CustomerDto(), CustomerList = result.Customers }; }

刷新方法的目的是调用FindAll服务方法来检索所有Customer实例,然后创建CustomerModel类的实例,这是CustomerView的模型。在第2行中,一个CustomerDto实例被分配给NewCustomerOperation属性。这与前端的客户详细信息部分绑定,使用双向模式。这意味着XAML能够在不需要任何其他额外代码的情况下更新CustomerDto实例;还不错。这是填充服务方法参数的一个好模式,就像在Save方法中看到的那样。

BootStrapper更改

在项目的这个阶段,只需要WPF客户端在单个域进程中调用服务方法,而不需要处理WCF服务的开销。

private void InitialiseDependencies() { GlobalContext.Instance().TransFactory = new TransManagerEntityStoreFactory(); Container.RequestContext = new RequestContextNaive(); ClientServiceLocator.Instance().CommandDispatcher = new DirectCommandDispatcher(); }

指示客户端想要使用GlobalContext服务中的内存实现TransFactory(第1行),并将RequestContext设置为简单的实现(第2行):RequestContextNaive类。还设置了CommandDispatcher,以便使用直接服务实现(第3行),在之前的文章中讨论过。

接近完成

如果运行应用程序并输入一些客户详细信息,然后按下保存按钮,注意到什么也没有发生。但如果调试代码,可以确认Save方法被执行了:

似乎一切都在正常工作;刷新方法在CreateNewCustomer服务方法执行后似乎工作得很好。

甚至可以检查结果,看到返回的集合包含一个客户,所有属性都正确填充。那么为什么前端没有被刷新呢?

这是WPF客户端应用程序的另一个特性;需要指示视图模型已经被刷新。为了做到这一点,需要引入INotifyPropertyChanged接口,在下一篇文章中介绍这个方面。

在本章中,介绍了RelayCommand类,这是Josh Smith实现的ICommand的一个实现。讨论了视图和ViewModel是如何设计的,以便前端可以使用XAML绑定调用服务方法。到目前为止,几乎已经让应用程序工作了,使用内存模式并在客户端域进程中调用服务器方法;目前避免了WCF和NHibernate组件,以方便起见。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485