You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure CosmosDB:无需读取项,仅当Etag不同时执行UpsertItemAsync

解决方案

你的核心问题是混淆了UpsertItemAsync和ReplaceItemAsync的行为,以及ETag参数的生效范围:

需求拆解

你要实现的是:仅对已存在且未被修改的项执行更新操作,不插入新项,也不覆盖已被其他客户端修改的项,同时避免提前读取记录。

问题根源

UpsertItemAsync的设计逻辑是「存在则更新,不存在则插入」——即使设置了IfMatchEtag,当目标项不存在时,CosmosDB会直接忽略ETag校验执行插入,这就是你看到记录总是被替换/插入的原因。

正确实现方案

使用ReplaceItemAsync配合IfMatchEtag参数,该方法仅针对已存在的项执行更新,不会插入新项,且严格校验ETag:

var database = _cosmosClient.GetDatabase("mydatabase");
var container = database.GetContainer("mycontainer");

var partitionKey = new PartitionKey("abcdef");
var requestOptions = new ItemRequestOptions
{
    // 仅当现有项的ETag与指定值完全匹配时才允许更新
    IfMatchEtag = "00000000-0000-0000-c718-abcdefghabcdef",
    // 可选:关闭写入后的内容返回,减少网络开销
    EnableContentResponseOnWrite = false
};

var model = new { id = "abcdef", property = "some value" };

try
{
    await container.ReplaceItemAsync(model, model.id, partitionKey, requestOptions, ct);
}
catch (CosmosException ex)
{
    // 处理两种核心异常:
    switch (ex.StatusCode)
    {
        case System.Net.HttpStatusCode.PreconditionFailed:
            // ETag不匹配,说明项已被其他客户端修改,不执行更新
            break;
        case System.Net.HttpStatusCode.NotFound:
            // 项不存在,不执行任何操作
            break;
        default:
            // 其他异常(如权限、网络问题),重新抛出
            throw;
    }
}

补充说明

  1. ETag的正确使用:ETag由CosmosDB服务端生成,每次项的内容变更都会更新,你需要从可信来源获取(如之前的缓存、业务流程中留存的ETag),而非手动设置或从Explorer复制(仅用于测试)。
  2. 如果需要插入分支:若你确实需要「存在则更新(ETag匹配),不存在则插入」的逻辑,可继续使用UpsertItemAsync + IfMatchEtag——此时当项不存在时会自动插入,存在时则校验ETag再更新。
  3. 模型中的_etag字段:客户端设置_etag = null无意义,CosmosDB在写入时会忽略客户端提供的_etag,仅使用服务端生成的值。

内容的提问来源于stack exchange,提问作者mihails.kuzmins

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 02:22:04