Implications of Batching

The IDataServiceUpdateProviderinterface is designed to support Batching, allowing customers to update many Resources in one transaction.

Indeed because the interface itself is low level – there is a API call needed to update every individual property – even just updating a single resource can result in a batch of API calls that should happen atomically: if an API call fails we need to rollback / abort whatever happened earlier.

All this means you can expect to see a number of calls to methods like IDataServiceUpdateProvider.SetValue(..), before each SaveChanges() call.

This seems simple enough, but it has profound implications on how you implement the interface.

You can’t just blindly apply the requested change as it is made. You need a way of recording and then committing or aborting all requested changes as a group.

If your data is in a database this typically means recording a series of commands and issuing them in a single transaction. Which something like the Entity Framework does for you automatically.

If however your data is memory, like the sample we’ve been building, you basically have to record intent, and apply that intent only when SaveChanges() is called.

This is why our implementation creates and records Actions whenever one of these low level changes is made…

Making our DSPContext Updatable.

You can do this in a million different ways, but for the purpose of illustration I’ve decided to add some new abstract methods, to our DSPContent class, for Creating Resources, Adding Resources, Deleting Resources and Saving Changes, like this: public abstract object CreateResource(ResourceType resourceType);

Implement IDataServiceUpdateProvider

As you can see it is a generic class where T is the type of our DataSource.

The IDataServiceUpdateProvider interface doesn’t have any methods to give us the DSPContext - so we’ll need to get it via the constructor somehow – remember we are in charge of constructing the provider through our implementation of IServiceProvider. :)

Metadata would be handy too, so the DSPUpdateProvider constructor takes both metadata and query providers as arguments:

As you can see the ReallySetValue (sorry for the unimaginative name) uses reflection to set the property.

You might be tempted to remove this Reflection code to can speed this code up, but before you do all that work remember the real latency is related to OData / Atom serialization and deserialization and network latency, so it might not actually be worth the effort.

Getting Property Values

Occasionally during updates Data Services needs to Get the value of a property, if so it will use the IDataServiceUpdateProvider.GetValue(..) method. Because this GetValue is non-mutating we don’t can just go and get the value directly using a little reflection code:

That’s because we don’t have any Relationships or ETags, so these methods will never be called, meaning we can safely ignore them for now.

Don’t worry though I’ll cover them in a future post.

Hooking it all together

The last step is to modify our IServiceProvider implementation to construct our DSPUpdateProvider and returned it whenever Data Services asked for the IDataServiceUpdateProvider interface.

Summary

The key to implementing IDataServiceUpdateProvideris realizing that you need to be able to delay execution. You can do that using either Proxy objects or Actions, like the above implementation.

Once you know how you are going to handle the atomicity requirements, and you understand what each method is supposed to do, it is actually a pretty mechanical exercise of going through an implementing each method in turn.