【问题标题】:Add or replace entity in Azure Table Storage在 Azure 表存储中添加或替换实体
【发布时间】:2011-05-26 21:21:44
【问题描述】:

我正在使用 Windows Azure 表存储并且有一个简单的要求:添加一个新行,用该 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吗?这似乎很慢。肯定有更好的方法吗?

【问题讨论】:

    标签: azure upsert azure-table-storage


    【解决方案1】:

    正如您所发现的,您不能只添加具有相同行键和分区键的另一个项目,因此您需要运行查询来检查该项目是否已存在。在这种情况下,我发现查看Azure REST API documentation 以了解存储客户端库可用的内容很有帮助。您会看到insertingupdating 有不同的方法。 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 年 9 月的更新,Microsoft 已更新 Azure 表 API 以包含两个 upsert 命令,Insert or Replace EntityInsert or Merge Entity

    【讨论】:

    • 谢谢,我确实考虑过,但这意味着这段代码必须知道需要复制的每个属性(或者实体本身需要一个CopyTo 方法)并且它需要每当属性更改时更新。我想我宁愿支付删除的费用并确保安全。
    • 好吧,尽管我不想这样做,但我最终还是这样做了。无论我如何尝试删除并重新插入实体,它都无法正常工作 - 一次它会成功,下一次会因一个错误而失败,下一次会因另一个错误而失败。
    • 我觉得这很令人惊讶。您是在尝试将添加和删除都保存为一个 .Save() 调用的一部分,还是在执行 .AddObject(); 。保存(); .DeleteObject(); .Save();
    【解决方案2】:

    为了使用带有 ReplaceOnUpdate 选项的 Delete 或 SaveChanges 对不受 TableContext 管理的现有对象进行操作,您需要调用 AttachTo 并将对象附加到 TableContext,而不是调用指示 TableContext 尝试插入它的 AddObject .

    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;
    

    【讨论】:

    • 我认为您可能需要了解幕后真正发生的事情。据我所知,底层 REST API 无法在一次调用中处理添加和删除。因此,当您只调用 .Save() 一次时,它将向存储服务发送两次调用。
    【解决方案4】:

    Insert/Merge or Update 于 2011 年 9 月添加到 API。这是一个使用 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
                    }
                });
        }
    

    【讨论】:

    • 这段代码中有一个问题,与乐观并发有关。您可能会认为“InsertOrReplace”的工作方式与“Insert”和“Replace”类似,只要自上次检查后记录没有更改,它就会进行 Upsert。但那是错误的。 “InsertOrReplace”实际上意味着“无论如何都覆盖该记录,并忽略乐观并发检查”。因此,如果您不关心乐观并发,那就太好了!否则,您可能不想将其用作“Upsert”。
    【解决方案5】:

    Storage API 不允许在组事务中对每个实体进行多个操作(删除+插入):

    一个实体在事务中只能出现一次,并且只能对其执行一次操作。

    MSDN: Performing Entity Group Transactions

    所以实际上你需要先阅读并决定插入或更新。

    【讨论】:

      【解决方案6】:

      您可以在微软官方 Azure.Data.Tables TableClient 中使用UpsertEntityUpsertEntityAsync 方法。


      完整的工作示例可在https://github.com/Azure-Samples/msdocs-azure-data-tables-sdk-dotnet/blob/main/2-completed-app/AzureTablesDemoApplicaton/Services/TablesService.cs 获得--

      public void UpsertTableEntity(WeatherInputModel model)
      {
          TableEntity entity = new TableEntity();
          entity.PartitionKey = model.StationName;
          entity.RowKey = $"{model.ObservationDate} {model.ObservationTime}";
      
          // The other values are added like a items to a dictionary
          entity["Temperature"] = model.Temperature;
          entity["Humidity"] = model.Humidity;
          entity["Barometer"] = model.Barometer;
          entity["WindDirection"] = model.WindDirection;
          entity["WindSpeed"] = model.WindSpeed;
          entity["Precipitation"] = model.Precipitation;
      
          _tableClient.UpsertEntity(entity);
      }
      

      【讨论】:

        猜你喜欢
        • 2017-07-25
        • 2013-10-14
        • 2021-07-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-11-06
        • 2017-10-18
        相关资源
        最近更新 更多