从Cosmos DB删除文档时出现Resource not found错误的排查求助
问题描述
我编写了C#代码,试图删除Azure Cosmos DB中指定日期范围内的文档。查询能正常返回目标文档,且在Azure Data Explorer中也能找到对应ID和分区键的文档,但执行删除时全部失败,抛出404错误。
代码实现
public async Task<int> DeleteDocumentsByDateRangeSimple(DateTime fromDate, DateTime toDate) { int deletedCount = 0; try { using CosmosClient client = new CosmosClient($"AccountEndpoint={endpoint};AccountKey={accountKey}"); Microsoft.Azure.Cosmos.Database db = client.GetDatabase("telemetrydata"); Container container = db.GetContainer("iothub"); string sfromDate = fromDate.ToString("yyyy-MM-dd\THH:mm:ss"); string stoDate = toDate.ToString("yyyy-MM-dd\THH:mm:ss"); var query = new QueryDefinition( "SELECT c.id, c.partitionKey FROM c WHERE c.Body.TimeStamp >= @minDate AND c.Body.TimeStamp <= @maxDate" ) .WithParameter("@minDate", sfromDate) .WithParameter("@maxDate", stoDate); using FeedIterator<dynamic> feed = container.GetItemQueryIterator<dynamic>(query); while (feed.HasMoreResults) { var response = await feed.ReadNextAsync(); foreach (var doc in response) { try { string id = doc.id?.ToString()?.Trim('"'); string partitionKey = doc.partitionKey?.ToString()?.Trim('"'); if (string.IsNullOrEmpty(id)) { Debug.WriteLine($"Skipping document with missing id"); continue; } var deleteResponse = await container.DeleteItemAsync<dynamic>(id, new PartitionKey(partitionKey)); if (deleteResponse.StatusCode == System.Net.HttpStatusCode.NoContent) { deletedCount++; } else { Debug.WriteLine($"Delete returned status {deleteResponse.StatusCode} for id={id}, pk={partitionKey}"); } } catch (Exception ex) { Debug.WriteLine($"Failed to delete document id={doc.id}, partitionKey={doc.partitionKey}: {ex.Message}"); } } } } catch (Exception ex) { Debug.WriteLine($"DeleteDocumentsByDateRangeSimple() : Unexpected error : {ex.Message}"); } return deletedCount; }
错误日志
Failed to delete document id=6aa773ff-54cb-4837-9620-653d58a2bf5f, partitionKey=VK_THC_GreenRoom: Response status code does not indicate success: NotFound (404); Substatus: 0; ActivityId: a6858546-110a-45e6-8fec-e56ef7571b44; Reason: ( Errors : [ "Resource Not Found. Learn more: https://aka.ms/cosmosdb-tsg-not-found" ] );
可能的原因及解决办法
分区键字段匹配错误
这是最常见的原因:你查询中获取的c.partitionKey可能并不是容器实际配置的分区键字段。比如你的容器分区键可能是/Body/DeviceId或其他字段,而非名为partitionKey的字段。删除操作必须使用正确的分区键值才能定位到文档,用错字段自然会返回404。
解决:登录Azure门户,查看目标容器的分区键配置,修改查询语句,选择正确的分区键字段(比如如果分区键是/DeviceId,查询就写SELECT c.id, c.DeviceId FROM c ...),删除时用该字段的值作为PartitionKey参数。动态类型解析导致的键值失真
使用dynamic类型接收查询结果时,容易出现类型解析问题。比如doc.id或doc.partitionKey的ToString()可能返回带引号的字符串(如果底层是JSON对象的字符串值),或者解析后的类型与实际文档的键类型不匹配(比如分区键是数字,你却转成了字符串)。
解决:定义强类型类来接收查询结果,避免动态类型的不确定性:public class DocIdentifier { public string id { get; set; } // 这里的字段名要和容器实际分区键字段一致 public string DeviceId { get; set; } }然后将查询迭代器改为
FeedIterator<DocIdentifier>,直接获取正确的键值,无需额外的Trim('"')操作。分区键类型不匹配
如果文档的分区键是非字符串类型(比如数字、布尔值),你将其转为字符串后传入PartitionKey,会导致定位失败。Cosmos DB对分区键的类型严格匹配,字符串类型的"123"和数字类型的123会被视为不同的分区键。
解决:检查文档分区键的实际类型,创建PartitionKey时使用对应类型的值,比如分区键是数字就传入new PartitionKey(123),而非字符串。容器或数据库引用错误
虽然概率较低,但要确认代码中指定的数据库名telemetrydata和容器名iothub与你在Data Explorer中查看的完全一致,避免误操作了其他环境的资源。
内容的提问来源于stack exchange,提问作者László Frank

