Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / IIS

WCF by Example - Chapter III - Response

4.96/5 (19 votes)
24 Oct 2012CPOL5 min read 101K  
WCF service response message patterns
PreviousNext
Chapter IIChapter IV
Image 3

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:

wcfbyexample_chapter03/CommonNewClasses.gif

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:

wcfbyexample_chapter03/Response_PublicProperties.png

wcfbyexample_chapter03/Response_PublicMethods.png

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:

wcfbyexample_chapter03/Response_PrivateFields.png

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:

wcfbyexample_chapter03/ClassDiagram.gif

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:

wcfbyexample_chapter03/ICustomerService_Chapter01.png

We need to re-factor CustomerDto so inherits from DtoBase and a new CustomerDtos class was created, also inheriting from the DtoBase class:

wcfbyexample_chapter03/CustomerDto.gif

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:

C#
[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:

C#
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:

C#
[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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)