16

我正在使用 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它吗?这似乎很慢。肯定有更好的方法吗?

4

6 回答 6

16

正如您所发现的,您不能只添加具有相同行键和分区键的另一个项目,因此您需要运行查询以检查该项目是否已存在。在这种情况下,我发现查看Azure REST API 文档以了解存储客户端库可用的内容很有帮助。您会看到插入更新有不同的方法。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 月的更新,微软已经更新了 Azure 表 API 以包括两个 upsert 命令,插入或替换实体插入或合并实体

于 2010-12-17T03:02:09.317 回答
1

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

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

于 2010-12-17T01:14:29.290 回答
1

在我的情况下,不允许先删除它,因此我这样做,这将导致一个事务到服务器,该事务将首先删除现有对象,然后添加新对象,消除复制属性值的需要

       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;
于 2010-12-29T14:41:09.077 回答
1

插入/合并或更新于 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
                }
            });
    }
于 2013-03-29T18:31:16.030 回答
0

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

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

请参阅MSDN:执行实体组事务

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

于 2011-08-11T10:57:16.270 回答
0

你可以使用微软官方 Azure.Data.Tables 中的UpsertEntity和方法。UpsertEntityAsyncTableClient


完整的工作示例可在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);
}
于 2021-10-13T12:39:01.123 回答