Wednesday, July 31, 2013

WCF: Serialization and Generics




Recently, I have been spending some time blogging about serialization.  This is partially in an effort to go back over some of the basics prior to starting on WCF and P2P in a week or so.  Today, I am continuing the serialization subject by taking a closer look at generics in regards to serialization.
This is actually a popular subject for a lot of newcomers to WCF.  Many developers who are fond of generics will create a service that exposes some generic method.  When they finally get ready to fire up the service and give it a trial run, they are quickly disappointed to discover that it doesn't work as expected.
So, what's the deal?
WCF does not support the use of generic methods for service operation.  In other words, only concrete types can be used for service operations.  Open generic types cannot be used.  Now, it is important to clarify this is not a limitation of WCF.  Rather, it is a limitation of WSDL, which is used to expose service metadata to consumers.  There is no construct within WSDL to define a generic type.  Generally speaking, I agree with this behavior since it decreases the coupling between a client and service. 
Although generic methods are not supported, it is possible to expose generic objects for the purpose of exchanging data.  However, there are some limitations.  Let's take a closer look:

Bounded Generics
In his book Programming WCF Services , Juval Lowy points out that it is possible to use "bounded generics."  This is sometimes referred to as closed generics.  Basically, it refers to defining a generic class for your data contract, but it is restricted to a specific type in the service operation.  This may sound somewhat vague.  So, here is an example to provide a better illustration:


[DataContract]
public class MyGenericObject<T>
{
    private T _id;
    private string _description;

    public MyGenericObject()
    {
    }

    [DataMember]
    public T ID
    {
        get { return _id; }
        set { _id = value; }
    }

    [DataMember]
    public string Description
    {
        get { return _description; }
        set { _description = value; }
    }
}

[ServiceContract(Namespace = "http://jeffbarnes.net/2007/05/boundedgenerics/")]
public interface IBoundedGenerics
{
    [OperationContract]
    MyGenericObject<int> GetGenericObject(int id);
}
 
 
As you can see, a generic object (MyGenericObject<T>) is exposed to the client.  However, the service operation restricts the usage to being of type integer.  This is what Juval Lowy means by "bounded generics."  However, it should be noted the client will not see the object as a generic.  When the metadata is generated to describe the service operation, the data contract and service operation will appear as a normal non-generic class.


[DataContract]
public class MyGenericObjectOfint
{
    private int _id;
    private string _description;

    public MyGenericObjectOfint()
    {
    }

    [DataMember]
    public int ID
    {
        get { return _id; }
        set { _id = value; }
    }

    [DataMember]
    public string Description
    {
        get { return _description; }
        set { _description = value; }
    }
}
 
The resulting name is due to an applied naming pattern that consists of:

Generic Class Name + "Of" + Type Parameter Name + Hash
 
The hash is added under certain conditions to reduce the risk of a name collision.  However, you should be aware the hash can create a really ugly class name.  It could be something like: MyGenericObjectOfSomeTypegDh87uV.  Obviously, this isn't an ideal name for the client to use.  Fortunately, you can override the use of the hash by specifying the Name property of the DataContract.  It supports parameters that correspond to the generic type parameters.  For example:


[DataContract(Name = "MyGenericObjectUsing{0}"]
public class MyGenericObject<T>
 
 
Using this approach, it is still possible to leverage generics from within the service implementation, but there is nothing special going on from the client's perspective.

You can download the sample code from here.

Ref:  Jeff W. Barnes

No comments:

Post a Comment