Добавить или заменить объект в хранилище таблиц Azure
Я работаю с Windows Azure Table Storage и имею простое требование: добавьте новую строку, перезаписав любую существующую строку с помощью этого PartitionKey/RowKey. Однако сохранение изменений всегда вызывает исключение, даже если я передаю параметр ReplaceOnUpdate:
tableServiceContext.AddObject(TableName, entity);
tableServiceContext.SaveChangesWithRetries(SaveChangesOptions.ReplaceOnUpdate);
Если объект уже существует, он бросает:
System.Data.Services.Client.DataServiceRequestException: An error occurred while processing this request. ---> System.Data.Services.Client.DataServiceClientException: <?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<code>EntityAlreadyExists</code>
<message xml:lang="en-AU">The specified entity already exists.</message>
</error>
Нужно ли мне сначала вручную запрашивать существующую строку и называть DeleteObject
на ней? Это кажется очень медленным. Наверняка есть лучший способ?
Ответы
Ответ 1
Как вы уже нашли, вы не можете просто добавить другой элемент с тем же ключом строки и ключом раздела, поэтому вам нужно будет запустить запрос, чтобы проверить, существует ли элемент уже. В таких ситуациях я нахожу полезным обратиться к API Azure REST API, чтобы узнать, что доступно для клиентской библиотеки хранилища. Вы увидите, что существуют специальные методы для inserting и обновление. ReplaceOnUpdate влияет только на то, что вы обновляете, а не вставляете.
В то время как вы можете удалить существующий элемент, а затем добавить новый, вы можете просто обновить существующий (сохранение одной поездки туда и обратно на хранение). Ваш код может выглядеть примерно так:
var existsQuery = from e
in tableServiceContext.CreateQuery<MyEntity>(TableName)
where
e.PartitionKey == objectToUpsert.PartitionKey
&& e.RowKey == objectToUpsert.RowKey
select e;
MyEntity existingObject = existsQuery.FirstOrDefault();
if (existingObject == null)
{
tableServiceContext.AddObject(TableName, objectToUpsert);
}
else
{
existingObject.Property1 = objectToUpsert.Property1;
existingObject.Property2 = objectToUpsert.Property2;
tableServiceContext.UpdateObject(existingObject);
}
tableServiceContext.SaveChangesWithRetries(SaveChangesOptions.ReplaceOnUpdate);
РЕДАКТИРОВАТЬ: Хотя на момент написания статьи было правильно, с обновлением за сентябрь 2011 года Microsoft обновила API-интерфейс Azure, чтобы включить две команды upsert, Вставить или заменить объект и Вставить или объединить сущность
Ответ 2
Чтобы работать с существующим объектом, не управляемым TableContext с параметрами Delete или SaveChanges с параметрами ReplaceOnUpdate, вам необходимо вызвать AttachTo и прикрепить объект к TableContext, вместо вызова AddObject, который инструктирует TableContext пытаться вставить его.
http://msdn.microsoft.com/en-us/library/system.data.services.client.dataservicecontext.attachto.aspx
Ответ 3
в моем случае не было разрешено сначала удалить его, поэтому я делаю это так, это приведет к одной транзакции на сервер, которая сначала удалит существующий объект, а затем добавит новый, удалив необходимость скопировать значения свойств
var existing = from e in _ServiceContext.AgentTable
where e.PartitionKey == item.PartitionKey
&& e.RowKey == item.RowKey
select e;
_ServiceContext.IgnoreResourceNotFoundException = true;
var existingObject = existing.FirstOrDefault();
if (existingObject != null)
{
_ServiceContext.DeleteObject(existingObject);
}
_ServiceContext.AddObject(AgentConfigTableServiceContext.AgetnConfigTableName, item);
_ServiceContext.SaveChangesWithRetries();
_ServiceContext.IgnoreResourceNotFoundException = false;
Ответ 4
API хранения не допускает более одной операции для каждого объекта (delete + insert) в групповой транзакции:
Сущность может появляться только один раз в транзакции, и только одна операция может быть выполнена против нее.
см. MSDN: Выполнение транзакций группы Entity
Поэтому на самом деле вам нужно сначала прочитать и принять решение о вставке или обновлении.
Ответ 5
Вставка/слияние или обновление была добавлена в API в сентябре 2011 года. Вот пример использования Storage API 2.0, который легче понять то как это делается в 1.7 api и ранее.
public void InsertOrReplace(ITableEntity entity)
{
retryPolicy.ExecuteAction(
() =>
{
try
{
TableOperation operation = TableOperation.InsertOrReplace(entity);
cloudTable.Execute(operation);
}
catch (StorageException e)
{
string message = "InsertOrReplace entity failed.";
if (e.RequestInformation.HttpStatusCode == 404)
{
message += " Make sure the table is created.";
}
// do something with message
}
});
}