Nov
13

Building Extensible WCF Service Interfaces With DataContractResolver

By kellabyte  //  Programming  //  11 Comments

I am currently working on a personal project which is purely for learning that involves many categories of skills. I have spent a long time programming on mobile devices so I decided it’s time to attack my weaknesses in my spare time.

I’ve decided to work on an implementation of the Scheduler-Agent-Supervisor pattern blogged about on Clemens Vasters’ (@clemensv) blog that you can find here.

During the initial work I am working on the Queue API’s for sending Job requests into the Scheduler via WCF and an MSMQ binding. I first started off in my first revision with a class called Job which contained a collection of IScheduledTask. I went with IScheduledTask because this way people using the library could implement their own custom versions of IScheduledTask and extend.

The great thing about this approach is the WCF service in theory can receive any task that derives from IScheduledTask. In reality though if the implementation of IScheduledTask exists outside of the assembly of this library WCF doesn’t know how to serialize/deserialize the messages because we are passing in a custom object structure.

In WCF there is a feature called Known Types. This is declarative and not as dynamic as we may want. In the .NET Framework v4 there was a new class addition called DataContractResolver which exposes the hooks we need to solve this problem more dynamically and seamlessly. Alternative we could implement a generic service, which poses a different set of problems I will cover in a future blog post. I felt this knowledge was worth getting out there as there is always more than 1 way to solve a problem.

First let’s replicate the problem before we try to solve it.

Create 2 projects, one called ServiceLib and another called ServiceClient. Add a reference to ServiceLib in the ServiceClient project. Inside ServiceLib let’s add some types.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
public interface IScheduledTask
{
[DataMember]
Guid TaskId { get; set; }
}
 
// This is a generic implementation of IScheduledTask.
[DataContract]
public class ScheduledTask<T> : IScheduledTask
{
[DataMember]
public Guid TaskId { get; set; }
 
[DataMember]
public T Value { get; set; }
 
public static implicit operator T(ScheduledTask<T> that)
{
return that.Value;
}
}
 
// This is our service contract which takes IScheduledTask.
[ServiceContract(Namespace = "http://ServiceLib.TaskService")]
public interface IScheduledTaskService
{
[OperationContract(IsOneWay = true)]
void Submit(IScheduledTask task);
}
 
// This is our actual service implementation.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class ScheduledTaskService : IScheduledTaskService
{
public void Submit(IScheduledTask task)
{
Console.WriteLine("We got a task!");
}
}

So what we have done here is declared a basic contract and an interface of IScheduledTask but in the concrete implementation in ScheduledTask<T> we added an extensibility hook by making it generic and having a property named Value on line 15 to store the extensible payload. This allows clients to submit a vast array of Tasks which contain custom parameters and information. On line 17 we included a nice little trick courtesy of Clemens that is pretty neat that I will explain further down.

In the ServiceClient project, let’s define 2 types called OrderTask and ShipmentTask :

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
[DataContract]
public class OrderTask
{
public OrderTask(int taskPriority)
{
this.TaskPriority = taskPriority;
}
 
[DataMember]
public int TaskPriority { get; set; }
}
 
[DataContract]
public class ShipmentTask
{
public ShipmentTask(string address)
{
this.Address = address;
}
 
[DataMember]
public string Address { get; set; }
}

Now let’s host the service and send a Task into it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
class Program
{
static void Main(string[] args)
{
ScheduledTaskService service = new ScheduledTaskService();
ServiceHost host = new ServiceHost(service);
host.Open();
 
IScheduledTaskService client = CreateClient("net.tcp://localhost:1212");
ScheduledTask<OrderTask> task = new ScheduledTask<OrderTask>();
task.Value = new OrderTask(2);
client.Submit(task);
 
Console.ReadKey();
}
 
private static IScheduledTaskService CreateClient(string endpointAddress)
{
EndpointAddress address = null;
address = new EndpointAddress(endpointAddress);
 
Binding binding = new NetTcpBinding(SecurityMode.None);
 
ChannelFactory<IScheduledTaskService> factory =
new ChannelFactory<IScheduledTaskService>(binding, address);
 
IScheduledTaskService service = factory.CreateChannel();
 
ContractDescription cd = factory.Endpoint.Contract;
}
}

As mentioned the problems arise when you send an implementation of IScheduledTask that is defined in another assembly. The service does not know how to deserialize these types. If you run the application you will see this exception as the result:

There was an error while trying to serialize parameter http://ServiceLib.TaskService:task. The InnerException message was ‘Type ‘ServiceLib.Task`1[[ServiceClient.OrderTask, ServiceClient, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]’ with data contract name ‘TaskOfOrderTasklAHeAqST:http://schemas.datacontract.org/2004/07/ServiceLib’ is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types – for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.’.  Please see InnerException for more details.

Cue DataContractResolver to help handle this for us.

Just to note, this is probably not the most efficient way to solve this but I thought it was pretty slick. The implementation could probably be far more cleaner. It’s enough to describe how this all works. There are various ways to implement DataContractResolver which may vary depending on your needs. With that said let’s do it!

I decided to create an attribute so that I could easily mark which classes are types that are used as payloads to ScheduledTask<T>. This allows our implementation of DataContractResolver to know which types to support.

1 2 3 4 5 6 7 8 9 10 11 12 13 14
public class TaskValueAttribute : Attribute
{
public TaskValueAttribute()
{
this.TypeNamespace = "http://tempuri.org/";
}
 
public TaskValueAttribute(string typeNamespace)
{
this.TypeNamespace = typeNamespace;
}
 
public string TypeNamespace { get; set; }
}

The reason we need to define a namespace in this attribute is if you think of the serialization/deserialization process, as the objects get represented in XML the serialization process only knows types within the assembly the service is defined. It won’t know what to do with an unexpected XML element within the serialized XML. This allows you to associate XML namespaces, with your external types offered by the DataContractResolver implementation we will provide.

Let’s mark our OrderTask and ShipmentTask classes with the new TaskValue attribute we just built.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
[DataContract]
[TaskValue("http://mynamespace.com")]
public class OrderTask
{
public OrderTask(int taskPriority)
{
this.TaskPriority = taskPriority;
}
 
[DataMember]
public int TaskPriority { get; set; }
}
 
[DataContract]
[TaskValue("http://mynamespace.com")]
public class ShipmentTask
{
public ShipmentTask(string address)
{
this.Address = address;
}
 
[DataMember]
public string Address { get; set; }
}

Now let’s implement the DataContractResolver.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
public class TaskDataContractResolver : DataContractResolver
{
private Dictionary<string, Type> typesByNamespace = null;
private Dictionary<Type, string> typesByType = null;
 
public TaskDataContractResolver()
{
typesByNamespace = new Dictionary<string, Type>();
typesByType = new Dictionary<Type, string>();
ConstructTypeMap();
}
 
private void ConstructTypeMap()
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
var availableTypes = from t in assembly.GetTypes()
where t.GetCustomAttributes(
typeof(TaskValueAttribute),
true).Count() > 0
select t;
 
foreach (Type type in availableTypes)
{
object[] attributes = type.GetCustomAttributes(
typeof(TaskValueAttribute), true);
 
if (attributes.Length > 0)
{
TaskValueAttribute taskContent =
attributes[0] as TaskValueAttribute;
 
string ns = taskContent.TypeNamespace + type.Name;
 
typesByNamespace.Add(ns, type);
typesByType.Add(type, ns);
}
}
}
 
}
 
// Serialization
public override bool TryResolveType(Type type, Type declaredType,
DataContractResolver knownTypeResolver,
out System.Xml.XmlDictionaryString typeName,
out System.Xml.XmlDictionaryString typeNamespace)
{
Type[] genericTypes = type.GetGenericArguments();
 
string ns = string.Empty;
Type genericType = null;
 
foreach (Type genType in genericTypes)
{
if (typesByType.ContainsKey(genType) == true)
{
typesByType.TryGetValue(genType, out ns);
genericType = genType;
break;
}
}
 
if (string.IsNullOrEmpty(ns) == false)
{
XmlDictionary dictionary = new XmlDictionary();
typeName = dictionary.Add(genericType.Name);
typeNamespace = dictionary.Add(ns);
 
return true;
}
else
{
return knownTypeResolver.TryResolveType(
type, declaredType, null, out typeName, out typeNamespace);
}
}
 
// Deserialization
public override Type ResolveName(string typeName, string typeNamespace,
Type declaredType, DataContractResolver knownTypeResolver)
{
Type type = null;
typesByNamespace.TryGetValue(typeNamespace, out type);
 
if (type != null)
{
Type genType = typeof(ScheduledTask<>);
Type genericType = genType.MakeGenericType(type);
return genericType;
}
else
{
// Defer to the known type resolver
return knownTypeResolver.ResolveName(
typeName, typeNamespace, null, knownTypeResolver);
}
}
}

On line 13 in the ConstructTypeMap method we use some reflection and LINQ to query all available types marked with our TaskValue attribute from every assembly in the AppDomain (this could probably be done a different way) and register the type and the namespace in 2 dictionaries that facilitate quick lookup’s during the serialization/deserialization processes.

Starting on line 45 we implement the TryResolveType method which uses one of the dictionaries to pull the information we require to fill out the typeName and typeNamespace parameters passed in to the method.

On line 81 our implementation of ResolveName begins. We again use the other dictionary provided to do a quick lookup for getting the type by the namespace value. On the lines 89 and 90 we create a generic type definition based on the type we resolved from the dictionary (that was decorated with the TaskValue attribute) and then return the constructed type.

Viola that is all the magic required. The only piece remaining is putting our TaskDataContractResolver into the operation behavior of each endpoint.

Let’s modify the ServiceClient to include attaching the TaskDataContractResolver to both the service endpoints and the client. We are going to change Program.cs to the following:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
class Program
{
static void Main(string[] args)
{
ScheduledTaskService service = new ScheduledTaskService();
ServiceHost host = new ServiceHost(service);
AttachDataContractResolver(host.Description.Endpoints[0]);
host.Open();
 
IScheduledTaskService client =
CreateClient("net.tcp://localhost:1212");
 
// Submit an order.
ScheduledTask<OrderTask> task = new ScheduledTask<OrderTask>();
task.Value = new OrderTask(2);
 
// Submit a shipment.
ScheduledTask<ShipmentTask> task2 = new ScheduledTask<ShipmentTask>();
task2.Value = new ShipmentTask("Parliament");
 
client.Submit(task);
client.Submit(task2);
 
Console.ReadKey();
}
 
private static IScheduledTaskService CreateClient(string endpointAddress)
{
EndpointAddress address = null;
address = new EndpointAddress(endpointAddress);
 
Binding binding = new NetTcpBinding(SecurityMode.None);
 
ChannelFactory<IScheduledTaskService> factory =
new ChannelFactory<IScheduledTaskService>(binding, address);
 
IScheduledTaskService service = factory.CreateChannel();
 
AttachDataContractResolver(factory.Endpoint);
 
return service;
}
 
private static void AttachDataContractResolver(ServiceEndpoint endpoint)
{
ContractDescription cd = endpoint.Contract;
 
foreach (OperationDescription opdesc in cd.Operations)
{
DataContractSerializerOperationBehavior serializerBehavior =
opdesc.Behaviors.Find<DataContractSerializerOperationBehavior>();
 
if (serializerBehavior == null)
{
serializerBehavior =
new DataContractSerializerOperationBehavior(opdesc);
 
opdesc.Behaviors.Add(serializerBehavior);
}
 
serializerBehavior.DataContractResolver =
new TaskDataContractResolver();
}
}
}

What we changed here was adding a new method named AttachDataContractResolver and we call this for both the service endpoint and the client endpoint. Now if we run our application the client’s call to the service serializes properly, and is received and deserialized in the service as well.

Now as you see we can even submit 2 implementations of IScheduledTask and everything is handled properly. This method may not be suitable for everyone but it solves an interesting problem.

Remember that implicit operator we implemented in ScheduledTask<T>? What this allows us to do is cast from ScheduledTask<OrderTask> to OrderTask implicitly even though the OrderTask is defined within the ScheduledTask<OrderTask>.Value property. Pretty neat!

In a soon to come blog post I’ll show an alternative way to do this using a fully generic implementation where we remove IScheduledTask completely. There are pros and cons to this but I thought it might be interesting to blog as well. Stay tuned!

  • tec-goblin

    Very interesting. Just a small correction: it’s voilà, and not viola ;) (the latter is an instrument of course).
    I suppose there is no declarative way to attach the datacontractresolver in the web config?

    • Rocky

      I stumbled across your article in doing a proof of concept with improvement in WCF with .Net 4.0. Thanks for the info, very helpful. I also just read another one of your posts and am curious about some of your other mobile phone experience : Specifically, how the adopt mobile phones (besides the blackberry) into the work environment. If you have sometime, I would really appreciate it.

  • Sumit Gupta

    nice article, but got One question:

    [ServiceContract(Namespace = "http://ServiceLib.TaskService")]
    public interface IScheduledTaskService
    {
    [OperationContract(IsOneWay = true)]
    void Submit(IScheduledTask task);
    }

    Operation contract does not allow you to pass object type and Interface is nothing more than a Object type.
    So can this work..??
    I am fairly new to WCF world so please let know, If I am missing anything.

  • Wesley Harris

    Two things:
    1. The LINQ expression .Count() > 0 can be replaced with .Any() which is (almost always) faster as it stops evaluating the IEnumerable as soon as it finds an element (which satisfies the condition – depending on the overload you use).

    2. Correct me if I am wrong, but I would imagine that even implementing the DataContractResolver, the deserialization will still fail if the service and the client run in different App Domains, as (typically) the service will not have the client implementation of IScheduledTask loaded in its App Domain. This problem stems from you only scanning the currently loaded Types in the App Domain. I can easily see how this can be extended to a plugin-like scanning mechanism to account for this, or even an IoC solution which gives a configurable “Known Types” implementation. Just thought I’d mention it

    Otherwise, nice article on the DataContractResolver. I would have liked to have seen it a while ago when I could have used it in a project I was writing :)

  • Ben

    I’m sure that the KnownTypes attribute has an overloaded constructor that allows you to provide a method to call which returns a collection of known types.

    I can’t remember and I can’t be bothered to check but you might want to look at it.

  • http://slappyza.wordpress.com/ Slappy

    You know I wander if this would be able to be extended to a service based NO-SQL/Entity Framework/Document store solution. From an extensibility perspective class definitions could be dropped on the server then queried through a pretty generic interface.

  • http://www.riagenic.com Scott Barnes

    I so stumbled upon this tonight and i’m like “WAY TO GO KELLY!!!” thx for saving me a whole world of Google pain.

  • David

    I am pulling several technologies together, and attempting to relate some terms. Is a ‘Task’ equivalent to UnitOfWork which we will typically observe in a Repository Pattern?

  • aaron

    could the use of generics be switched around? i.e. instead of receiving a type that contains a generic value, could you send back a type that internally holds a generic value?

  • Prakash Mishra

    Hi all,

    I have been getting error in ServiceClient project for the Type

    ScheduledTask.

    I have given referece for ServiceLib in ServiceClient project after adding service Reference.
    and where I am declaring my Main method in that class I am importing as follow..

    using ServiceClient.ScheduledTaskService;

    as the namespace of the service Reference.

    Notes: My both project as a WCF Library project.

    I am fairly new in WCF. Kindly look into it