使用Microsoft.Azure.Cosmos 3.20.1调用ReadNextAsync抛出JSON序列化错误
Cosmos DB 3.20.1 查询大集合时反序列化Id冲突问题解决方案
根本原因
你遇到的错误和Cosmos DB内存储的文档id字段无关,是Newtonsoft.Json的引用跟踪机制和旧版Cosmos SDK内置序列化器的缓存复用逻辑冲突导致的:
- 3.20.1版本的Cosmos SDK内置的Newtonsoft序列化器默认开启了引用跟踪,会在反序列化时把JSON里的
$id标识符存入全局缓存 - 大数据量查询需要多次调用
ReadNextAsync拉取多页数据,同一序列化器实例会复用同一缓存 - 不同分页的JSON返回结果中存在相同的
$id值时,就会触发A different value already has the Id冲突,小数据量单页即可拉取完成时不会触发该问题
解决方案
方案1:自定义序列化器禁用引用跟踪(最稳定兼容)
自定义Cosmos序列化器,显式关闭Newtonsoft的引用跟踪配置,彻底避免跨分页的引用ID冲突:
public class CustomCosmosSerializer : CosmosSerializer { private readonly JsonSerializer _jsonSerializer; public CustomCosmosSerializer() { _jsonSerializer = new JsonSerializer { // 核心配置:禁用引用跟踪 PreserveReferencesHandling = PreserveReferencesHandling.None, ReferenceLoopHandling = ReferenceLoopHandling.Ignore, // 可自行添加其他序列化配置,如命名策略、日期格式化规则等 }; } public override T FromStream<T>(Stream stream) { using (stream) using (var streamReader = new StreamReader(stream)) using (var jsonReader = new JsonTextReader(streamReader)) { return _jsonSerializer.Deserialize<T>(jsonReader); } } public override Stream ToStream<T>(T input) { var memoryStream = new MemoryStream(); using (var streamWriter = new StreamWriter(memoryStream, leaveOpen: true)) using (var jsonWriter = new JsonTextWriter(streamWriter)) { _jsonSerializer.Serialize(jsonWriter, input); streamWriter.Flush(); } memoryStream.Position = 0; return memoryStream; } }
在你的GetCosmosClientOptionsClientOptions方法中注入自定义序列化器:
protected static CosmosClientOptions GetCosmosClientOptionsClientOptions(int maxRetryAttemptsOnThrottledRequests = 3, int maxRetryWaitTimeInSeconds = 180) { return new CosmosClientOptions() { ConnectionMode = Microsoft.Azure.Cosmos.ConnectionMode.Direct, RequestTimeout = new TimeSpan(0, 1, 0), MaxTcpConnectionsPerEndpoint = 1000, MaxRetryAttemptsOnRateLimitedRequests = maxRetryAttemptsOnThrottledRequests, MaxRetryWaitTimeOnRateLimitedRequests = TimeSpan.FromSeconds(maxRetryWaitTimeInSeconds), AllowBulkExecution = true, // 注入自定义序列化器 Serializer = new CustomCosmosSerializer() }; }
方案2:升级Cosmos SDK版本
3.20.1之后的3.x稳定版已经修复了内置序列化器的缓存复用问题,你可以直接将Microsoft.Azure.Cosmos包升级到3.38.1及以上的最新3.x稳定版本,无需修改业务代码即可解决该问题。
临时规避方案
如果暂时无法做版本升级或序列化器修改,可以在QueryRequestOptions中调小单次拉取的条目数,降低冲突概率:
var requestOptions = new QueryRequestOptions { MaxItemCount = 100 };
该方案仅为临时缓解,大数据量场景下仍有可能触发冲突。
内容的提问来源于stack exchange,提问作者SauravBhattacharya
相关产品推荐
相关产品推荐

