First Time WCF Client Contracts = Nightmare!

By | October 27, 2013

In one of my projects (WPF, WCF) we are working with server and client side entities. Although the entities look very similar, we had some reasons to separate them:
– Client side entities require more logic
-> Implementation of INotifyPropertyChanged
-> Implementation of IEditableObject
-> Restore handling (When user cancels edit)
All these functionalities were implemented in a base class in the client. When working with WCF and WPF I always prefer the share the service contract interfaces rather than creating client proxies. (The code is cleaner and it gives us more flexibility.) But in this project, we couldn’t share the contracts, because the WCF contract interfaces always references the service side entities.
To solve the problem I came up with different approaches:

Sharing the service contracts between server and client (or creating a client proxy) and using a mapper mapper framework (AutoMapper) to map the entities into our client entities.
Adding the service contracts as “linked” files in the client and working with compilation conditions to “swap” the server side contract/entity namespaces. (Or using the same server entity namespace in the client).
Generating with a script the server contracts + entities in the client. Exchanging the base class of the entities. Deserializing the retrieved data directly into client entities.

I decided to implement the third approach because:
– We don’t need and extract step for mapping between server and client entities.
– Having full control over the contracts and entities. (In case of linked files we still have some restrictions)
– “Real” client entities with client specific base class.

For the implementation of these I decided to use T4. My T4 script runs through my server contracts + entities and generates in the client the “duplicate” code. All generated interfaces classes are partial. So we still can add some additional code. And because the DataContractSerializer relies on the contract namespace and NOT on the CLR namespace, we can use client specific namespace for the generated files. To make sure that all server/client contract interfaces and entities are using the same data contract namespace, I added the ContractNamespace attribute in both assemblies.

// Client assembly
[assembly: ContractNamespace("http://blog.micic.ch/contractexample", ClrNamespace = "ContractExampleClient")]

// Server assembly
[assembly: ContractNamespace("http://blog.micic.ch/contractexample", ClrNamespace = "ContractExampleService")]

So, and here is some very important information!

The DataContractSerializer creates instances of the client entities without calling the constructor. (System.Runtime.Serialization.FormatterServices.GetUninitializedObject) Initialization code must be placed in a [OnDeserializing] or [OnDeserialized] method.
The order of the entity properties (DataMembers) must be the same as on the server. By default, the serializes orders the properties alphabetical. BUT if you have a base class, the serializer orders first the properties of your base class alphabetical and then the properties of the child class. (It’s possible to explicitly define the order -> [DataMember(Order=…)] But that’s too much work for an lazy developer :)

Unfortunately… the WCF error messages aren’t very helpful when your client contracts are wrong. In worst case, you don’t get any exception. I lost at the end 6 hours until I found out that:
– My client entities weren’t initialized correctly because the constructor wasn’t called.
– Not all properties were initialized because the order was wrong. My client entity base class has less properties than the server entity base class. And that screwed up the ordering of the properties “in some” of the entities.

So in short. Make sure that:
– The server and client contract namespaces are the same.
– You are using [OnDeserializing] or [OnDeserialized] methods to initialize your client entity.
– The ordering of the base/child entity classes is correct.

At the end, I was very happy with the solution. The DataContractSerializer is very flexible! In our client entities we have for example ObservableCollection<> properties which are mapping to IEnumerable<> server entities. And the handling with different base interfaces, classes and generics is very good!

The first time you will struggle a little bit. But afterwards you will love to work with server and client side WCF contracts.

Here is an simplified example of your server and client code:


/////// SERVER
namespace ContractExampleService
{
    [ServiceContract]
    public interface IPersonService
    {
        [OperationContract]
        Person[] Get();
    }

    [DataContract]
    public abstract class EntityBase
    {
        [DataMember] public TKey Id { get; set; }
        [DataMember] public DateTime CreatedAt { get; set; }
    }

    [DataContract]
    public class Person : EntityBase
    {
        [DataMember] public string Firstname { get; set; }
        [DataMember] public string Lastname { get; set; }
        [DataMember] public IEnumerable
Addresses { get; set; } } public class Address : EntityBase { public string Street { get; set; } } } /////// CLIENT namespace ContractExampleClient { [ServiceContract] public interface IPersonService { [OperationContract] Person[] Get(); } [DataContract] public abstract class EditableEntity : IEditableObject { public enum EState { NotChanged = 0, Created = 1, Changed = 2, Deleted = 3 } [DataMember] public TKey Id { get; set; } [DataMember] public DateTime CreatedAt { get; set; } public EState State { get; set; } public void BeginEdit() { } public void CancelEdit() { } public void EndEdit() { } } [DataContract] public class Person : EditableEntity { [DataMember] public string Firstname { get; set; } [DataMember] public string Lastname { get; set; } [DataMember] public ObservableCollection
Addresses { get; set; } } [DataContract] public class Address : EditableEntity { [DataMember] public string Street { get; set; } } }

Leave a Reply

Your email address will not be published. Required fields are marked *


6 − = three