Tuesday, August 26, 2008

Concurrency with SSDS via REST

Eugenio already covered concurrency via the SOAP interface with with latest post.  The idea is exactly the same in REST, but the mechanics are slightly different.  For REST, you specify a "Etag" value and either the If-Match or If-None-Match headers.

Here is a simplified client that does a PUT/POST operation on SSDS:

internal void Send(Uri scope, string etag, string method, string data,
    Action<string, WebHeaderCollection> action, Action<WebException> exception)
{
    using (var client = new WebClient { Credentials = _credentials })
    {
        client.Headers.Add(HttpRequestHeader.ContentType, "application/x-ssds+xml");

        if (etag != null)
            client.Headers.Add(HttpRequestHeader.IfMatch, etag);

        client.UploadStringCompleted += (sender, e) =>
        {
            if (e.Error != null && exception != null)
            {
                exception((WebException)e.Error);
            }
            else
            {
                if (action != null)
                {
                    action(e.Result, client.ResponseHeaders);
                }
            }
        };

        client.UploadStringAsync(scope, method, data);
    }
}

 

All this does is add the If-Match header and the Etag (which corresponds to the Flexible Entity Version system attribute).  This instructs the system to only update if the version held in SSDS matches the version specified in the Etag with the If-Match header.

Failure of this condition will result in a 412 error "A precondition, such as Version, could not be met".  You simply need to handle this exception and move on.

Next, there are times when you have a large blob or a largish flexible entity.  You only want to perform the GET if you don't have the latest version.  In this case, you specify the Etag again with the If-None-Match header.

Here is a simplified client that shows how the GET would work:

public void Get(Uri scope, string etag,
    Action<string, WebHeaderCollection> action, Action<WebException> exception)
{
    using (var client = new WebClient { Credentials = _credentials })
    {
        client.Headers.Add(HttpRequestHeader.ContentType, "application/x-ssds+xml");

        if (etag != null)
            client.Headers.Add(HttpRequestHeader.IfNoneMatch, etag);

        client.DownloadStringCompleted += (sender, e) =>
        {
            if (e.Error != null && exception != null)
            {
                exception((WebException)e.Error);
            }
            else
            {
                if (action != null)
                    action(e.Result, client.ResponseHeaders);
            }

        };

        client.DownloadStringAsync(scope);
    }

When you add the Etag and this header, you will receive a 304 error "Not Modified" if the content has NOT changed since the Etag value you sent.

I am attaching a small Visual Studio sample that includes this code and demonstrates these techniques.