WCF 示例 – 第三章 – 响应






4.96/5 (18投票s)
WCF 服务响应消息模式
![]() |
![]() |
|
第二章 | 第四章 |
![]() |
我目前正在为一个 Metro 应用程序做一个新项目:Sharpy。我打算在 Sharpy 应用程序的服务端使用“WCF 示例”文章中讨论的模式;目的是展示 Metro 应用程序的开发与我们迄今为止看到的应用程序类型有多么相似。目前还没有代码,但希望很快就会有所改变。希望大家喜欢。 文章在这里。 |
系列文章
WCF 示例是一系列文章,描述了如何使用 WCF 进行通信和 NHibernate 进行持久化来设计和开发 WPF 客户端。 系列简介 描述了文章的范围,并从高层次讨论了架构解决方案。
章节概述
在“第一章 - 基线”中,定义了 CustomerService
的草稿版本,该服务使用 WCF 合同模式定义。我们解释了使用 DTO 作为客户端和服务器之间传递信息的理由。
在本章中,我们将讨论建立服务方法标准的需求,该标准允许服务器通知业务警告和异常。一种可能的方法是利用现有的 WCF 功能,但这将导致服务和业务逻辑与 WCF 耦合。我们倾向于采用“独立于 WCF”的设计,因此我们可以开发、探索和测试我们的应用程序,而无需部署 WCF。我们将在后面的章节中看到这种方法的优势。
本章的源代码可以在 Codeplex 更改集 67446 中找到。eDirectory
解决方案的最新代码可以在 Codeplex 中找到。
响应模式
eDirectory
应用程序服务是基于请求/响应的。正如我们在上面指出的,我们将重新定义服务方法,使它们始终将一些公共数据返回给客户端。这样,服务器就可以使用此机制向客户端指示警告或/和异常。
解决方案是让所有服务方法都返回实现接口(IDTOResponse
)的 DTO。该接口公开一个属性(Response
),其中包含与警告和异常相关的所有必需信息。在本章中,我们将向解决方案添加以下文件
Response
类公开两个布尔标志,指示服务方法是否返回了警告或/和业务异常。响应实例包含一个警告集合和一个业务异常,以便客户端可以使用它们来通知用户。这两种类型都是简单的 DTO,便于 WCF 序列化。对于调用时创建新域实例的服务方法,还提供了一个 EntityId
属性。Response
类的方法和 public
属性是
![]() |
|
Response
类是我们为数不多的使用自定义 WCF 序列化标记的类之一,该类仅公开只读属性,因此需要自定义序列化才能使用 private
字段。这是一个标记有用的好例子,但是我们不会对业务 DTO 遵循这种做法,因为它可能导致难以调试的讨厌的 WCF 序列化问题。对于我们的 DTO,我们不需要任何特殊的标记,因为 .NET 可以自行解决序列化问题。下面,我们可以看到该类是如何被标记的
我们提到,我们所有的服务方法都将返回至少一些最小数据,指示服务器端是否发生了警告或/和异常。Response
类包含客户端需要了解警告或异常的所有信息。因此,所有服务方法都返回实现 IDtoResponseEnvelop
接口并继承自 DtoBase
类的 DTO。如果服务不需要返回任何额外数据,则提供 DtoResponse
类。下面的类图描述了本章涵盖的类和接口之间的关系
服务重构
需要修改 CustomerService
以符合新的消息模式。自第一章以来,我们没有修改过该服务。服务接口是
我们需要重构 CustomerDto
,使其继承自 DtoBase
,并创建了一个新的 CustomerDtos
类,该类也继承自 DtoBase
类
唯一剩下的就是替换 FindAll
方法签名,使其返回 CustomerDtos
实例而不是 CustomerDto
实例的列表
[ServiceContract(Namespace = "http://wcfbyexample/customerservices/")]
public interface ICustomerService
{
[OperationContract]
CustomerDto CreateNewCustomer(CustomerDto customer);
[OperationContract]
CustomerDto GetById(long id);
[OperationContract]
CustomerDto UpdateCustomer(CustomerDto customer);
[OperationContract]
CustomerDtos FindAll();
}
让我们实现所有服务方法。到目前为止,我们只实现了 CreateNewCustomer
方法
public class CustomerService
:ICustomerService
{
public IRepositoryLocator Repository { get; set; }
#region ICustomerService Members
public CustomerDto CreateNewCustomer(CustomerDto dto)
{
var customer = Customer.Create(Repository, dto);
return Customer_to_Dto(customer);
}
public CustomerDto GetById(long id)
{
return Customer_to_Dto(Repository.GetById<customer>(id));
}
public CustomerDto UpdateCustomer(CustomerDto dto)
{
var instance = Repository.GetById<customer>(dto.CustomerId);
instance.Update(Repository, dto);
return Customer_to_Dto(instance);
}
public CustomerDtos FindAll()
{
var customers = Repository.FindAll<customer>();
var result = new CustomerDtos {Customers = new List<customerdto>()};
if (customers.Count() == 0) return result;
customers.ToList().ForEach(c => result.Customers.Add(Customer_to_Dto(c)));
return result;
}
#endregion
private CustomerDto Customer_to_Dto(Customer customer)
{
return new CustomerDto
{
CustomerId = customer.Id,
FirstName = customer.FirstName,
LastName = customer.LastName,
Telephone = customer.Telephone
};
}
}</customerdto></customer></customer></customer>
在这个阶段,我们的服务已经成型,但是我们将在后面的章节中看到,当采用“工作单元”模式时,这段代码将如何演变。应该注意到的一点是,在领域实体到 DTO 的转换是持续发生的。我们可能会在后面的章节中看到使用像 AutoMapper
这样的框架来优化我们设计中的这个领域。
新测试
我们在服务中添加了一些新功能,应该创建一些新测试
[TestMethod]
public void UpdateCustomer()
{
CreateCustomer();
var id = CustomerInstance.CustomerId;
var dto = new CustomerDto
{
CustomerId = id,
FirstName = "Joe",
LastName = "Bloggs",
Telephone = "8888-8888"
};
CustomerInstance = Service.UpdateCustomer(dto);
Assert.IsTrue(CustomerInstance.CustomerId == id,
"Customer Id should have remained the same");
Assert.AreSame(CustomerInstance.Telephone, "8888-8888",
"Incorrect telephone after the update");
}
[TestMethod]
public void FindAll()
{
CreateCustomer();
var result = Service.FindAll();
Assert.IsTrue(result.Customers.Count == 1, "One customer instance was expected");
}
章节总结
在本章中,我们涵盖了服务方法的响应方面。我们不想依赖 WPF 异常机制来进行警告和异常处理,因此我们创建了一个由我们的服务返回的响应对象。我们还看到了这种方法对我们的 DTO 和服务定义有什么影响。
在未来的章节中,我们将更深入地阐述响应对象的使用方式。在此之前,我们需要在服务器端定义事务管理器,在客户端定义服务适配器。