Home > WCF > DataContract Constructors in WCF

DataContract Constructors in WCF

When svcutil.exe is used to generate the proxy for a WCF service, it creates a copy of the service contract (interface) and the data contracts used by that service contract. If you use the DataContractSerializer on the service (the default option), you are implying that you want to share the contracts and not the types so the copy of the data contract created by svcutil.exe only includes the fields and properties decorated with DataMemberAttribute and does not include other members of the type like the constructors. This means that any additional constructor(s) you may have created for the data contract on the server side will not be present on the client.

So how should you design the application if you want to use the constructors on the client side?

  • If you want to have a constructor with the same signature and implementation on the client, then sharing the types is an option. You can share the types by adding a reference to the assembly containing the data contract. You can then instruct svcutil.exe to use the referenced types instead of creating copies of those types using the /reference option (which is the default behaviour when you use "Add Service Reference" in Visual Studio). You need to understand that sharing the types will result in type fidelity and you will need to change the data contract on both sides every time you modify it. If you are building a service-oriented application, then you should try to avoid sharing the types as it does not follow the "Share the contract, not the type" tenet of service orientation.
  • If you want to have one or more constructors on the client side but these constructors are not the same as those available on the server side, you can use svcutil.exe to generate the copy of the data contract on the client and then add the constructors you need on the client side. If you look at the type definition for the data contract svcutil.exe generates on the client, you will notice that it is marked as partial. So you can create a new file and define the constructors you want to add to the partial class. Note that you should not make any modifications to the generated class as you will lose your changes when you regenerate the class and in fact, this is one of the main reasons why partial classes are supported by the compilers.

Regardless of the approach you choose, what you need to be aware of is that the parameter-less constructor for the data contract is only called when the object is instantiated by your code. When you call a WCF service and the service returns a data contract, WCF runtime on the client side does not call the parameter-less constructor. This makes sense as the returned message includes all the state you need and there is no point in running the constructor.

Let’s have a look at an example. The Customer class is a data contract defined on the server side.

[DataContract]
public class Customer
{
    public Customer(int customerId)
    {
        this.ID = customerId;
    }

    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public int ID { get; set; }

    [DataMember]
    public DateTime LastModified { get; set; }
}


We don’t need any other constructor on the server side as the only place we create an instance of the customer class is in GetCustomer operation and the customer object is passed into the rest of operations.

public class CustomerService : ICustomerService
{
    public Customer GetCustomer(int customerID)
    {
        Customer customerData = new Customer(customerID);
        
        //TODO: 
        customerData.Name = "John Doe";
        return customerData;
    }

    public int CreateCustomer(Customer customer)
    {
        //TODO: Save customer to the DB and return the ID;
        return 1;
    }

    public Customer UpdateCustomer(Customer customer)
    {
        //TODO: Update the customer in DB
        customer.LastModified = DateTime.Now;
        return customer;
    }

}

   

As we said earlier, svcutil.exe does not generate any constructors on the client side but what we can do is to create a file that adds the required constructors to the data contract, which is defined as a partial class. The following code segment shows how to add the additional constructor to the partial class. Although we have now added a constructor (which takes a string parameter), this constructor will not be executed when receiving an instance of customer from the service for the reason mentioned above.

If you need to perform additional operations when the message is received by the client, you can use OnDeserializingAttribute or OnDeserializedAttribute defined in System.Runtime.Serialization namespace. We have also added two methods that are decorated with the serialization attributes,

public partial class Customer
{
    private int originalCustomerId;

    public Customer(string name)
    {
        this.Name = name;
    }

    [OnDeserialized]
    public void Deserialized(StreamingContext context)
    {
        originalCustomerId = ID;
    }

    [OnSerialized]
    public void Serialized(StreamingContext context)
    {
        if (ID != originalCustomerId)
            throw new InvalidOperationException();
} }

The constructor on the client side takes a string parameter as the client knows about the name of the new customer but not their ID. 

Customer newCustomer = new Customer("Joe Bloggs");
int customerID = customerService.CreateCustomer(newCustomer);

 

This is one of the benefits of a shared contract design as we can define a different constructor on the client side to suit the specific needs of the client application.  
WCF does not call the constructor when the customer object is deserialized and this is where the serialization attributes come into play to enable us to perform the initialisation. In this case, we want to make sure the code using the customer class on the client side does not change the ID for an existing customer so we record the original customer ID when the customer object is received by the client and compare the ID property with the original value when it is being serialized and sent back to the service.  

Customer customer = customerService.GetCustomer(1);
customer.ID = 2;
customerService.UpdateCustomer(customer);

  

In this post we saw difference between the constructors and the methods decorated with the serialization attributes. The constructors are called when you create instances of the data contracts in your code whereas the decorated methods are called by the WCF runtime before (or after, depending on the attribute you are using) the data contract object is serialized/deserialized.

Categories: WCF
  1. No comments yet.
  1. No trackbacks yet.
You must be logged in to post a comment.
Follow

Get every new post delivered to your Inbox.