I need to be able to insert an entity to an azure storage table under these conditions:
I made this simple program to test, but I can't figure out how to get this to work. it never reaches the exception. (I thought this was regular ETag behavior requirements).
Note that if I use Insert operation instead of InsertOrReplace I get an exception even if ETag has an unchanged value.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(storageConnString);
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
tableClient.RetryPolicy = new LinearRetry(TimeSpan.FromSeconds(3), 10);
var testtable = tableClient.GetTableReference("davidstesttable");
testtable.CreateIfNotExists();
//insert first entity
var newentity = new DynamicTableEntity("idunno", String.Empty, "*", new Dictionary<string, EntityProperty> { { "testprop", new EntityProperty("testval") } });
Msg("insert initial entity");
testtable.Execute(TableOperation.InsertOrReplace(newentity));
Msg("inserted");
Msg("retrieving");
TableResult tableResult = testtable.Execute(TableOperation.Retrieve("idunno", String.Empty));
DynamicTableEntity firstRetrieve = (DynamicTableEntity)tableResult.Result;
Msg("retrieved. etag: " + firstRetrieve.ETag);
Msg("inserting the initial entity again to change the ETag in the table");
testtable.Execute(TableOperation.InsertOrReplace(newentity));
Msg("inserted");
Msg("retrieving");
TableResult tableResult2 = testtable.Execute(TableOperation.Retrieve("idunno", String.Empty));
DynamicTableEntity secondRetrieve = (DynamicTableEntity)tableResult2.Result;
Msg("retrieved. etag: " + secondRetrieve.ETag);
if(firstRetrieve.ETag != secondRetrieve.ETag)
{
Msg("confirmed entity in table now has different etag");
Msg("inserting the first retrieved. (etags should not match now, expecting StorageException)");
try
{
//If I use Insert operation instead of InsertOrReplace, I do get the exception,
//but I tested with this and then I get the exception even if the ETag is unchanged or * !
testtable.Execute(TableOperation.InsertOrReplace(firstRetrieve));
Msg("hmm should not have reached here!");
}
catch (StorageException e)
{
if(e.RequestInformation.HttpStatusCode == 409 || e.RequestInformation.HttpStatusCode == 412)
Msg("got exception as expected because of the mismatching ETag.");
}
}
I may have found a solution. Will accept this if nobody has a better answer.
I tried to add If-Match
header of the OperationContext
, with the Etag as the value. This worked. I thought this was an automatically added thing, but apparently not.
testtable.Execute(
TableOperation.InsertOrReplace(firstRetrieve),
null,
new OperationContext {
UserHeaders = new Dictionary<String, String>
{
{ "If-Match", firstRetrieve.ETag }
}
}
);
Now, when using null
as ETag, I can InsertOrReplace, and it also properly checks ETag if it's something else.
Note that if I use *
as ETag, I get a 404 exception if the entity doesn't exist. So use null
to get the intended functionality. Or just detect *
and don't add the header.
EDIT:
Caveat: If you'd want to insert a new item (ETag == null
) but still want to get an exception code 409 conflict if it already exists, you must use the Insert
operation instead of the InsertOrReplace
operation.