| | |
Chapter II | | Chapter IV |
| I am currently working on a new project for a Metro application: Sharpy. I intend to use the patterns discussed in the WCF by Example articles for the service side of the Sharpy application; the goal is to demonstrate how similar is the development of Metro applications to the type of applications we have seen so far. There is not code available yet but hopefully this will change soon. I hope you like it. The article is here.
|
The Series
WCF by example is a series of articles that describe how to design and develop a WPF client using WCF for communication and NHibernate for persistence purposes. The series introduction describes the scope of the articles and discusses the architect solution at a high level.
Chapter Overview
In "Chapter I - Baseline", a draft version of the CustomerService
was defined, the service was defined using the WCF contract patterns. We explained the reason for using DTOs as a way to pass information between the client and the server.
In this chapter, we will discuss the need for establishing a standard for the service methods that enables the server to notify for business warnings and exceptions. One possible approach is to leverage the existing WCF functionality but this would result in coupling the service and business logic to WCF. We would prefer an "agnostic-WCF" design therefore we can develop, explore and test our application without having to deploy WCF. We will see in later chapters the advantage of this approach.
The source code for this chapter can be found at Codeplex change set 67446. The latest code for the eDirectory
solution is found at Codeplex.
The Response Pattern
The eDirectory
application services are request/response based. As we indicated above, we will re-define the service methods so they always return some common data back to the client. In this way, the server can use this mechanism for indicating the client of warnings or/and exceptions.
The solution consists in having all the service methods to return DTOs implementing an interface (IDTOResponse
). This interface exposes a property (Response
) containing all the information that is required in relation to warnings and exceptions. In this chapter, we will add the following files to the solution:
The Response
class exposes two boolean flags indicating whether the service method returned warnings or/and a business exception or not. The response instance contains a collection of warnings and a business exception so the client can use them to notify the user. Both types are simple DTOs to facilitate the WCF serialization. An EntityId
property is also available for service methods that create a new domain instance when they are called. The Response
class methods and public
properties are:
|
|
The Response
class is one of the few that we will use customized WCF serialization tagging, the class exposes only read-only properties so custom serialization is required so the private
fields are used instead. This is a good example when tagging is useful, however we will not follow this practice for our business DTOs as it can incur nasty WCF serialization issues that are not easy to debug. For our DTOs, we will not require any special tagging as .NET can resolve the serialization by itself. Below, we can see how the class was tagged:
We mentioned that all our service methods will return at least some minimum data indicating if warnings or/and exceptions took place in the server side. The Response
class contains all the information that the client requires to be informed of warnings or an exception. Therefore all the service methods return DTOs that implement the IDtoResponseEnvelop
interface and inherit from the DtoBase
class. The DtoResponse
class is provided in the case the service does not need to return any additional data. The below class diagram describes the relationship between classes and interfaces covered in this chapter:
Service Re-factor
The CustomerService
needs to be modified so it complies with the new message pattern. We have not modified the service since Chapter I. The service interface was:
We need to re-factor CustomerDto
so inherits from DtoBase
and a new CustomerDtos
class was created, also inheriting from the DtoBase
class:
The only thing left is to replace the FindAll
method signature, so it returns an instance of CustomerDtos
instead of a list of CustomerDto
instances:
[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();
}
Let's implement all the service methods. We only implemented the CreateNewCustomer
method so far:
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>
At this stage our service is taking the right shape, however we will see in later chapters how this code evolves when the "unit of work" pattern is adopted. One aspect that should be noticed is the constant transformation that takes place between the domain entities to Dtos. We might see in later chapters the use of a framework as AutoMapper
to leverage this area in our design.
New Tests
We have added some new functionality to our service and we should create some new tests:
[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");
}
Chapter Summary
In this chapter, we have covered the response aspect of the service methods. We don't want to rely on WPF exception mechanisms for the purpose of warning and exception handling, so we created a response object that is being returned by our services. We also saw what implication this approach has on our DTO and service definitions.
In future chapters, we will elaborate in more depth about how the response object is used. Before that, we need to define our transaction manager at the server and the service adapter at the client.