Добавить или заменить объект в хранилище таблиц 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
                }
            });
    }