Sharing Data Contracts between clients & servers with WCF Data Services


I’ve been blogging a bit about the OData protocol put forward by Microsoft and even wrote an article about it on Code Project.  That article is supposed to be followed by others about WCF Data Services, the .NET implementation of OData, well…  stay tuned!

odata I love that OData protocol.  For me it finally brings forward the vision of web services replacing a database for data access and business logic.  What was missing from SOAP web services was the ability to query.  So yes, you could expose your data on the web, but you had to know in advance what type of query your clients will need.  If you were not sure, you would pretty much end up with the antics GetCustomerByID, GetCustomers, GetCustomersByContractID, GetCustomersByFirstName, SearchCustomerByName, GetCustomersWhoWasInTheLobbyWithAPipeWrench and the like.  All those web services were doing only one thing, being a tin adapter to your back-end store.  Each time I saw those it reminded me that Web Service was a young technology and SQL was a much more mature one.

With OData that changed a little since you can now query your web services, so one web service implementation should satisfy most of your client needs for read-operations.  Now, if like me you thought SOAP Web Services was a young and immature technology, wait until you meet OData and its .NET implementation WCF Data Services.

WCF Data Services has very little to do with WCF beside being an API for services exposed on the web.  Most of WCF pipeline is absent, the ABC (Address, Binding & Contract) of WCF is nowhere to be found and when it doesn’t work, you get a nice “resource not found” message on your browser as the only troubleshooting information.

Nevertheless, it does get the job done and exposes OData endpoints which are quite great and versatile.  With .NET 4.0, WCF Data Services got some improvements too.  A greater querying capabilities (e.g. ability to only count an entity set), interceptors and…  service operations.

Service operations really fills the gap missing between a SOAP web services with parameters and a simple OData where you expose an Entity set.  A service operations allows you to define an operation with parameters but where its result can be furthered queried (and filtered) by the client.

Now the client-side story isn’t as neat as WCF in general.  With WCF you can share your service and data contracts between your server and your client.  This isn’t always what you would like to do, but it’s a very useful scenario and allow you to share entities between the client and the server and enables you to share component dealing with those entities.  The key API there is ChannelFactory ; instead of using Visual Studio or a command-line tool to generate a proxy, you let that class generate it from your service contract.  It works very well.

This doesn’t exist with WCF Data Services.  You’re sort of force to generate proxies with Visual Studio or command tools.  This duplicates your types between your server and client and doesn’t allow you to have components (e.g. business logic) shared between the server and client using data entities.  As an extra, the generation tools don’t support Service Operations at all.

In order to use service operation on the client-side, you need to amend the generated proxy.  Shayne Burgess has a very good blog entry explaining how to do it.  Actually, I got inspired by that blog entry to write client proxies by hand, allowing us to share data entities across tiers.  Here is how to do it.

First you need a service operation.  You can learn how to do that here.  The twist we’re going to give here is to define an interface for the model, another one for the service operations and one containing both.  The reason to split those interfaces is due to the service operations being implemented in the data service directly while the non-service operations are in the data model itself.

public interface IPocQueryService : IPocQueryModel, IPocQueryOperations
{
}

public interface IPocQueryModel
{
    IQueryable<FileInfoData> Files { get; }
}

 

public interface IPocQueryOperations
{
    IQueryable<FileInfoData> GetFilesWithParties(int minPartyCount);
}

Now we can define our client-proxy.

public class PocQueryServiceProxy : DataServiceContext, IPocQueryService
{
    #region Constructor
    public static IPocQueryService CreateProxy(Uri serviceRoot)
    {
        return new PocQueryServiceProxy(serviceRoot);
    }

    private PocQueryServiceProxy(Uri serviceRoot)
        : base(serviceRoot)
    {
    }
    #endregion

    IQueryable<FileInfoData> IPocQueryModel.Files
    {
        get { return CreateQuery<FileInfoData>("Files"); }
    }

    IQueryable<FileInfoData> IPocQueryOperations.GetFilesWithParties(int minPartyCount)
    {
        return CreateQuery<FileInfoData>("GetFilesWithParties").AddQueryOption("minPartyCount", minPartyCount);
    }
}

On the client side, we can use the proxy as if we were talking directly to the DB.

var service = PocQueryServiceProxy.CreateProxy(builder.Uri);
var files = from m in service.Files
            where m.ID.Contains("M")
            orderby m.ID descending
            select m;
var files2 = service.GetFilesWithParties(2);

I’m not a huge fan of having so many interfaces for such a simple solution.  We could have only one and use it for the proxy only.  But then we would be loosely coupled with the server, which is what I am trying to avoid by sharing the interfaces on the client & server.

About these ads

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s