Wednesday, 10 March 2010

Using generics with WCF client proxy interfaces

In the spirit of trying to learn something new at least once in a while, I’ve discovered something about interfaces in .NET – i.e. that a class can inherit from multiple interfaces with the same method signatures and only have to implement the interfaces once. This may sound like a useless feature but it actually solves a problem that I have with WCF service contract factoring and generics (more on that later). To illustrate, let us consider the following interface:

interface IDataAccess<T>
where T : class
{
List<T> GetData();
}

A typical use for such an interface would be generic data access. GetData() will always return a list of T (whatever T might be). For the purposes of this example T will be a the Car class:

public class Car
{
public string Make { get; set; }
public string Registration { get; set; }
public int? Mileage { get; set; }

public override string ToString()
{
   return string.Format("{0}, registration {1}, with {2} miles on the clock",
       Make, Registration, Mileage.Value.ToString());
}
}

We can then provide a concrete implementation of IDataAccess<T>

public class CarDataAccess : IDataAccess<Car>
{
public List<Car> GetData()
{
   Car myCar = new Car()
   {
       Make = "Vauxhall Zafira",
       Mileage = 50000,
       Registration = "DY07 XXX"
   };

   List<Car> myList = new List<Car>();
   myList.Add(myCar);
   return myList;
}
}

Now we can also ask the class to implement an interface that, rather than using generics, references the concrete (Car) class:

interface ICarDataAccess
{
List<Car> GetData();
}

and change the first line of the data access class to reflect this:

public class CarDataAccess : IDataAccess<Car>, ICarDataAccess

The great thing is that not only does this compile, but we can use either interface to access the GetData() method of the concrete class:

ICarDataAccess myDataObject = new CarDataAccess();
List<Car> results = myDataObject.GetData();

Console.WriteLine(results[0].ToString());

IDataAccess<Car> myOtherDataObject = myDataObject as IDataAccess<Car>;
Debug.Assert(myOtherDataObject != null);

results = myOtherDataObject.GetData();

Console.WriteLine(results[0].ToString());

So why I am impressed by this - what possible practical use does it have? The answer is that it helps us to overcome on of the major limitations of WCF client proxies – specifically the lack of support for using generic interfaces in service contracts. For example, if you were to decorate the IDataAccess<T> interface with the [ServiceContract] attribute:

[ServiceContract]
interface IDataAccess<T>
where T : class
{
List<T> GetData();
}

and deploy the CarDataAccess class as a WCF service, then it would compile but would throw an InvalidOperationException when the service host started with an error message along the lines of:

The contract name ‘xxx’ could not be found in the list of contracts implemented by the service ‘yyy’.

So we have to have to use the non-generic interface on the service side. However we can use generics on the client side by manually adding the generic interface to the list of interfaces implemented by the client proxy:

public class CarDataAccessServiceClient : ClientBase<ICarDataAccess>, ICarDataAccess, IDataAccess<Car>
{
// generated proxy code here
}

This doesn’t break the proxy, but it does allow us to write generic client code:

public void DoSomething<T, D>()
where T : ICommunicationObject, IDataAccess<D>, IDisposable, new(),
where D : class
{
    T myProxy = new T();

    try
    {
       myProxy.Open();
       List<D> results = myProxy.GetData();
       myProxy.Close();
    }
    catch
    {
       myProxy.Abort();
    }
}

An easy trick, but one that is about to save me hours of coding.

No comments:

Post a Comment