问题
I need to be able to insert an entity to an azure storage table under these conditions:
- if it doesn't exist, insert.
- if it exists, but I specify ETag to be *, then replace.
- if it exists, but ETag has another value, then throw StorageException with code 409 or 412. (for example I would try to insert an entity I have retrieved, but it has been updated from elsewhere in the meantime)
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.");
}
}
回答1:
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.
来源:https://stackoverflow.com/questions/17487965/how-to-use-etag-to-throw-exception-on-insert-if-etag-doesnt-match-except-when