Monday 19 September 2011

Framework 4.0 - How to configure WCF Service to use DataContractSerializer or XmlSerializer

Default serialization mechanism in WCF is DataContractSerializer. Main benefit of the DataContractSerializer is that it is faster. It is good if you use .NET clients, but in some cases it requires to use different serializer. If you need support both - XmlSerializer and DataContractSerializer, you have following options:

1) create new interface for every serialization method, which will bring new end-points
2) Inherit service class from the same interface as you use in with DataContractSerializer, but mark with [XmlSerializerFormat]
3) Keep one interface and one end-point, but create separate operations for that, like GetOrders (for the DataContractSerializer) and GetOrders2 (for the XmlSerializer)
I have already created article about XmlSerializer and DataContractSerializer and expected results in serialization/deserialization.

Framework 4.0 - XmlSerializer vs DataContractSerializer. Prepare class for serialization. Performance.

Very important aspect of that article is how to prepare class for the serialization.
In this article I will describe how to configure WCF service to use DataContractSerializer or XmlSerializer on service level or operation level and how to test it during development process by using WCF Test Client.

I will not be focused on extracting data from back-end systems and will use randomly generated data.


Visual Studio 2010 Solution Definition

First of all, I created simple WCF solution.
which contains

1) WCF Service project contains
 CustomerService.svc - returns CustomerProfile to client
 OrderManagementService.svc - returns Customer orders

2) Business Logic Library, which contains
 customer.cs  - customer profile class
 Order.cs - Order class
 Enums.cs - Order Status enumeration
 OrderTrackingInfo.cs - Order Tracking information class



Final solution will look like:




Configure WCF Service to use proper serializer

As I described above, there is option to configure serialization mechanism on interface level or at operation level.

Set Serialization at interface level

As sample I decided to use GetCustomer operation, which returns Customer object.
In serialization article I described how to prepare class for different serialization methods.
In Framework 4.0 you can use class as is - it will work in both methods.

Use DataContractSerializer at interface level

If you use visual studio, you don't actually, have to do anything. DataContractSerializer is a default serialization mechanism for the WCF.
I am just digging in it a little bit more.
If you decide to use DataContractSerializer, you have to mark interface ICustomerService with [ServiceContract].
Every operation, which will be available for call you have to mark with [OperationContract]. It is a standard routine to create wcf service nowadays.

    [ServiceContract]
    public interface ICustomerService
    {
        [OperationContract]
        PerformanceTest.WCF.Service.BLL.Customer GetCustomer(string customerId);
 
        [OperationContract]
        bool CustomerExists(string customerId);
    }

In this case, both - GetCustomer and CustomerExists will use DataContractSerializer.
In my case service endpoint is located at following url:
http://localhost/PerformanceTest.WCF.Service/CustomerService.svc

Testing

Set WCF service project as start-up project and "CustomerService.svc" as startup file and press F5 in VS 2010. It will run WCF Test Client. You should see window on next picture.
You can make operation call in test client. If you double mouse click on operation on left panel, it will open operation call parameters on right side.
On the right panel you can see request and response. To make call, you have to enter appropriate input values  and click "invoke", as shown at picture below:



In the result you will see formatted service call response. You can switch from formatted mode to XML mode.
XML mode allows you to see raw SOAP request and response from service as shown below.



Here is sample SOAP service request and response.
Request:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/ICustomerService/GetCustomer</Action>
  </s:Header>
  <s:Body>
    <GetCustomer xmlns="http://tempuri.org/">
      <customerId>100</customerId>
    </GetCustomer>
  </s:Body>
</s:Envelope>

Response:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header />
  <s:Body>
    <GetCustomerResponse xmlns="http://tempuri.org/">
      <GetCustomerResult xmlns:a="http://schemas.datacontract.org/2004/07/PerformanceTest.WCF.Service.BLL" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <a:CustomerId>100</a:CustomerId>
        <a:CustomerMemo i:nil="true" />
        <a:FirstName i:nil="true" />
        <a:LastName i:nil="true" />
      </GetCustomerResult>
    </GetCustomerResponse>
  </s:Body>
</s:Envelope>

Use XmlSerializer at interface level

If you decide to use XmlSerializer at interface level in the same project - you have 2 options:
1) Create new interface ICustomerService2, mark it with the same attributes as in 1), plus add additional attribute at interface level [XmlSerializerFormat]. You can also remove unused operations, like CustomerExists.
   
    [ServiceContract]
    [XmlSerializerFormat]
    public interface ICustomerService2
    {
        [OperationContract]
        PerformanceTest.WCF.Service.BLL.Customer GetCustomer(string customerId);
    }


In this case, both - GetCustomer and CustomerExists will use XmlSerializer.
The service endpoint url is:
http://localhost/PerformanceTest.WCF.Service/CustomerService2.svc

Testing

To test "CustomerService2.svc" service, which uses Xml serialization you should set it as start-up file and press F5 in VS 2010.
It will run the same WCF Test Client. You can see one difference: CustomerExists operation disappeared from list of available
operations because we excluded it from interface.
You will see the difference now when you will try make call:
You have to select request body in dropdown listbox, shown below


enter CustomerId value and click "Invoke"



you can see XML request and response by clicking at "XML" tab:


2) inherite class from the same interface, but mark with [XmlSerializerFormat]

    [XmlSerializerFormat]
    public class CustomerService3 : ICustomerService
    {


Testing

To test "CustomerService3.svc" service, which uses Xml serialization you should set it as start-up file and press F5 in VS 2010.
It will run the same WCF Test Client. You will se the same list of operations as in first case, because it is inherited from the same interface.



Set serialization at operation level:

In sections above I described CustomerProfile service. In this section I will use different service - OrderManagementService.
There is only one endpoint in this case, but serialization mechanism will be defined at operation level.
The service endpoint url is:
http://localhost/PerformanceTest.WCF.Service/OrderManagementService.svc

Default operation serialization is based on settings at interface level. If interface not marked with [XmlSerializerFormat], all operations will use DataContracSerializer and the other way around.
If you want override default serializer at operation level, you have to use [XmlSerializerFormat] or [DataContractFormat].

In the sample below, I created new GetOrders2 operation and marked it with [XmlSerializerFormat]. It will force WCF service to use XmlSerializer on GetOrders2 call.

    [ServiceContract]
    public interface IOrderManagementService
    {
        [OperationContract]
        List<PerformanceTest.WCF.Service.BLL.Order> GetOrders(string customerId);
 
        [OperationContract]
        [XmlSerializerFormat]
        List<PerformanceTest.WCF.Service.BLL.Order> GetOrders2(string customerId);
 
        [OperationContract]
        PerformanceTest.WCF.Service.BLL.Enums.OrderStatus GetOrderStatus(int orderId);
 
        [OperationContract]
        PerformanceTest.WCF.Service.BLL.Order GetOrderByOrderId(int orderId);
    }


Testing

To test "OrderManagementService.svc" service, you should set it as startup file and press F5.
In WCF Test Client you will see GetOrders and GetOrders2 operations. The testing will be pretty same as customer testing.
Testing GetOrders - DataContractSerializer:



Testing GetOrders2 - XmlSerializer: