Sun, 30 Nov 2008

Deleting Entities from Windows Azure Without Querying First

The most obvious way of deleting an entity using the ADO.NET Data Services client library is to query first (via LINQ), and then call DeleteObject to delete the entity, like this:

            svc = new TestContext();
            var item = (from i in svc.TestTable
                        where i.PartitionKey == "item1pk"
                        && i.RowKey == "item1rk" select i).Single();
            svc.DeleteObject(item);
            svc.SaveChanges();

However, this means that a delete takes two roundtrips.  In some cases, you may know the unique key (partition key plus row key) of an entity already, and you’d like to delete without first querying.  There is a way to do this.  You need to construct a dummy local entity with the right partition and row keys, and then AttachTo this object, specifying an ETag of “*” (to prevent a conflict when your delete encounters the ETag of the real entity).  The code looks like this:

            svc = new TestContext();
            item = new TestEntity("item2pk", "item2rk");
            svc.AttachTo("TestTable", item, "*");
            svc.DeleteObject(item);
            svc.SaveChanges();

What do they look like over HTTP?

The first method (query-and-delete) looks like a GET followed by a DELETE.  Note the If-Match header in the DELETE which specifies the ETag returned during the GET.

GET /TestTable(PartitionKey='item1pk',RowKey='item1rk') HTTP/1.1 
User-Agent: Microsoft ADO.NET Data Services 
x-ms-date: Sun, 30 Nov 2008 06:02:27 GMT 
Authorization: SharedKeyLite smarxtest:yNeUvY5puNFfdMENJdHxRc1n5E/Qetlyyk9fIX/tVmM= 
Accept: application/atom+xml,application/xml 
Accept-Charset: UTF-8 
DataServiceVersion: 1.0;NetFx 
MaxDataServiceVersion: 1.0;NetFx 
Host: smarxtest.table.core.windows.net

HTTP/1.1 200 OK 
Cache-Control: no-cache 
Transfer-Encoding: chunked 
Content-Type: application/atom+xml;charset=utf-8 
ETag: W/"datetime'2008-11-30T06%3A02%3A53.1863478Z'" 
Server: Table Service Version 1.0 Microsoft-HTTPAPI/2.0 
x-ms-request-id: 24f43295-e6d5-45f6-abca-6da3b99fe758 
Date: Sun, 30 Nov 2008 06:02:52 GMT 
<…body omitted…>

DELETE /TestTable(PartitionKey='item1pk',RowKey='item1rk') HTTP/1.1 
User-Agent: Microsoft ADO.NET Data Services 
x-ms-date: Sun, 30 Nov 2008 06:02:27 GMT 
Authorization: SharedKeyLite smarxtest:yNeUvY5puNFfdMENJdHxRc1n5E/Qetlyyk9fIX/tVmM= 
Accept: application/atom+xml,application/xml 
Accept-Charset: UTF-8 
DataServiceVersion: 1.0;NetFx 
MaxDataServiceVersion: 1.0;NetFx 
Content-Type: application/atom+xml 
If-Match: W/"datetime'2008-11-30T06%3A02%3A53.1863478Z'" 
Host: smarxtest.table.core.windows.net 
Content-Length: 0

The second method (unconditional delete) looks like the above DELETE, but this time with an If-Match header that specifies the ETag “*”.  This means that the delete will ignore the entity’s ETag, instead of raising a conflict error.

DELETE /TestTable(PartitionKey='item2pk',RowKey='item2rk') HTTP/1.1 
User-Agent: Microsoft ADO.NET Data Services 
x-ms-date: Sun, 30 Nov 2008 06:02:27 GMT 
Authorization: SharedKeyLite smarxtest:sVSZmoN/bWw8mD8ZJ58abYkSjVvenmPukbFmMjmv3uQ= 
Accept: application/atom+xml,application/xml 
Accept-Charset: UTF-8 
DataServiceVersion: 1.0;NetFx 
MaxDataServiceVersion: 1.0;NetFx 
Content-Type: application/atom+xml 
If-Match: * 
Host: smarxtest.table.core.windows.net 
Content-Length: 0

Which method should you use?

This really depends on the application.  The key difference is that the first method ensures that nothing changed between when you queried for the entity and when you deleted it.  (This is based on the ETag check.)  This is useful if there’s a possibility that since you decided to delete the entity, something else has changed the entity (and you care about this conflict).

However, if your code is as simple as what I have above, and you’re not doing any checking of the item you pulled back from table storage, you may as well use the second method, which will unconditionally delete the entity no matter what has happened to it since. This has the added benefit of only requiring one round-trip to storage, rather than two.