N-Tier Synchronization With The Sync Framework And WCF ChannelFactory (Part 1)

Sync Services for ADO.NET 2.0 allows a proper n-tier architecture where you can synchronize a client with a back-end data store via a web service. To do this, your web service must expose a simple interface consisting of four operations. As described in the documentation, one of these operations must have the following signature:

 [OperationContract]
 SyncSchema GetSchema(Collection<string> tableNames, SyncSession syncSession);

When used in a SyncAgent, a proxy is used with ServerSyncProviderProxy like this:

 this.RemoteProvider = new ServerSyncProviderProxy(proxy);

One thing to notice about the ServerSyncProviderProxy constructor is that it takes as input any object (that is, an instance of System.Object). Obviously, you can't just pass in any object and expect it to work at run-time, so there's actually a sort of duck typing in play here.

As far as I can tell, ServerSyncProviderProxy uses Reflection to invoke the expected methods, such as the GetSchema method shown above.

That probably works well for a WCF proxy generated by Visual Studio (I don't know, because I haven't tried), but fails if you try to reference the service interface (let's call it ISyncService) directly and create a proxy using ChannelFactory<T>.CreateChannel.

 ChannelFactory<ISyncService> cf = 
     new ChannelFactory<ISyncService>(binding, serviceAddress.ToString());
 ISyncService proxy = cf.CreateChannel();
 this.RemoteProvider = new ServerSyncProviderProxy(proxy);

If you do that, you will get a run-time exception. What happens is that ServerSyncProviderProxy expects the GetSchema method to take a string[], not a Collection<string>. Although I have no authoritative explanation for this, it's not that surprising, since the default proxy code generator for WCF (svcutil, Visual Studio) will create proxies with lists of elements represented by arrays - you have to explicitly state that you want collections instead, if that's the case.

Lesson #1

If you want to create a proxy instance using ChannelFactory<T>, you will have to change the signature of the GetSchema method to

 [OperationContract]
 SyncSchema GetSchema(string[] tableNames, SyncSession syncSession);

That will enable you to use a proxy created by ChannelFactory<T>, but will obviously break for proxies generated by svcutil or Visual Studio. If you must support both scenarios, you will need to derive from ServerSyncProviderProxy and override the GetSchema method to do something more intelligent.

Update (2008-08-27): Part 2 is now available here.