WCF: Serialization and Generics

10. May 2007 15:29

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.

Comments

6/22/2007 11:16:30 AM #

I think I have ended up where you describe.


ie.


[DataContract(Name="MyClass")]


public class MyClass<T> { ... }


[ServiceContract]


public interface IMyService<T> {


   [Operation]


   public void MyMethod(T val) { ... }


}


public class MyService : IMyService<int> {


   public void MyMethod(int val) { ... }


}


Which I can't get working.  I thought this _would_ work given that I am binding the actual service implementation to a type before it is exposed via wsdl, but alas, no joy.


(the other alternative is that it does work, I just haven't figured out how to structure my web.config file yet)

Mark Allanson |

6/26/2007 8:37:11 PM #

Oh, by the way... the way I got around it for the moment is to alias the generic interface, at the interface level, and provide a concrete implementation of the aliased interface.

ie.

public interface ISpecialisedMyService : IMyService
{
}
public class SpecialisedMyService : ISpecialisedMyService
{
}

It adds another layer of indirection, but the aliased interface is essentially a single line of code that needs no change from this point forward.

Mark Allanson |

5/23/2012 5:26:22 AM #

pingback

Pingback from dkphp.com

Serialize Generic type over WCF Service | PHP Developer Resource

dkphp.com |

Comments are closed

About Me

I'm a passionate software developer and advocate of the Microsoft .NET platform.  In my opinion, software development is a craft that necessitates a conscious effort to continually improve your skills rather than falling into the trap of complacency.  I was also awarded as a Microsoft MVP in Connected Systems in 2008, 2009, and 2010.


Can’t code withoutThe best C# & VB.NET refactoring plugin for Visual Studio
Follow jeff_barnes on Twitter

View Jeff Barnes's profile on LinkedIn

 

Shared Items

Disclaimer

Anything you read or see on this site is solely based on my own thoughts.  The material on this site does not necessarily reflect the views of my employer or anyone else.  In other words, I don't speak for anyone other than myself.  So, don't assume I am the official spokesperson for anyone.