Monday, 11 February 2008

WCF Data Contract translation

In WCF you often pass data between the client and service in serializable classes marked up as Data Contracts. These classes are supposed to be data containers only and should not contain business logic. Whenever a class of this type is received on the service side is therefore translated into a business object of some sort before processing takes place.

A straight-forward way of doing this translation is to set up a little translator class with a method that takes a parameter of type X and returns an object of type Y, and to manually code the translation between the two in the method body. The drawback here is that you have to explicitly code the translation both from X to Y and from Y to X. I thought I had a better solution.

Because of an earlier, lengthy refactoring exercise a couple of Data Contracts (that were also business objects) were now split into two classes - one a Data Contract for the service, and one a Data Transfer Object for the data access and mapping layer. These two classes shared a number of properties and because of this their common interface was abstracted out. And this led to really easy translation, because the translator could simply translate treat two types implementing the same interface as the same type.

public static IProfile Translate(IProfile input)

This method allows the caller to control the types going in and coming out, through type casting.

//the object "input" is of type DataContracts.Profile
DataAccess.Profile = (DataAccess.Profile) Translator.Translate(input);


This works just fine for simple object-to-object translation (e.g. DataAccess.Address to DataContract.Address), but with nested objects I ran into trouble. For example, IProfile declares a public property of type List. With the above approach we run into trouble because there's no way of telling the Translate(IProfile) method what type the contained list of IAddress is.

So it became necessary to be a bit more type specific. The Translate() methods were rewritten with generics like so:


public static T Translate(IProfile input) where T : IProfile, new() {}
public static T Translate(IAddress input) where T : IAddress, new() {}
//... and so on


And now, by adding an extra property public Type AddressType {get; set;} on IProfile we could make use of the MethodInfo.MakeGenericMethod(Type) to call the correct method on Translator to translate the right type of Address for the IProfile object, like so:


// ... this is all in class Translator

public static T Translate(IAddress input) where T : IAddress, new()
{
//... translation logic for addresses goes here ...
}

public static T Translate(IProfile input) where T : IProfile, new()
{
T output = new T();

// ... do the member-specific translation here ...

if (input.AddressList.Count > 0)
{
foreach (IAddress inputAddress in input.AddressList)
{
MethodInfo method = typeof (Translator).GetMethod("Translate", new Type[]{inputAddress.GetType()});
MethodInfo genericMethod = method.MakeGenericMethod(output.AddressType);

output.AddressList.Add((IAddress) genericMethod.Invoke(null, new object[]{inputAddress}));
}
}

return output;
}

2 comments:

Thomas said...

" These two classes shared a number of properties and because of this their common interface was abstracted out. And this led to really easy translation, because the translator could simply translate treat two types implementing the same interface as the same type."

That is a great idea :) unfortunately this is not a option in my case, since I don't have full control over the buisness entities. How do you usually implement translation when buisness entities and datacontracts are very different? Have you tried Microsoft's entity translator? (http://msdn.microsoft.com/en-us/library/ff647889.aspx) Or do you think manualle mapping the objects is the best way?

Øyvind Valland said...

Hi Thomas,

I wrote this a while back - and have found a better way since then. Have a look at AutoMapper - http://automapper.codeplex.com/ - it will do the trick for you I think.