Добавить или заменить объект в хранилище таблиц Azure - PullRequest
15 голосов
/ 17 декабря 2010

Я работаю с 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 для нее? Это кажется очень медленным. Конечно, есть лучший способ?

Ответы [ 5 ]

16 голосов
/ 17 декабря 2010

Как вы обнаружили, вы не можете просто добавить другой элемент с тем же ключом строки и ключом раздела, поэтому вам нужно будет выполнить запрос, чтобы проверить, существует ли элемент уже.В подобных ситуациях полезно посмотреть документацию API Azure REST , чтобы узнать, что доступно клиентской библиотеке хранилища.Вы увидите, что существуют отдельные методы для вставки и обновления . 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, Вставить или заменить объект и Вставить или объединить объект

1 голос
/ 29 марта 2013

Вставка / Слияние или Обновление была добавлена ​​в 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
                }
            });
    }
1 голос
/ 29 декабря 2010

в моем случае не было разрешено сначала удалить его, поэтому я делаю это следующим образом, это приведет к одной транзакции на сервер, которая сначала удалит существующий объект, а затем добавит новый, избавив от необходимости копировать значения свойств

       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;
1 голос
/ 17 декабря 2010

Чтобы работать с существующим объектом, НЕ управляемым TableContext с опциями Delete или SaveChanges с параметрами ReplaceOnUpdate, вам нужно вызвать AttachTo и прикрепить объект к TableContext, вместо вызова AddObject, который инструктирует TableContext попытаться вставить его.

http://msdn.microsoft.com/en-us/library/system.data.services.client.dataservicecontext.attachto.aspx

0 голосов
/ 11 августа 2011

API хранилища не допускает более одной операции на объект (удаление + вставка) в групповой транзакции:

Объект может появляться только один раз в транзакции, и только одна операция может бытьвыступил против этого.

см. MSDN: Выполнение транзакций группы объектов

Так что на самом деле вам нужно сначала прочитать и принять решение о вставке или обновлении.

...