Azure Cosmos DB UpsertDocumentAsync C# API 工作原理及操作疑问
UpsertDocumentAsync 工作机制及代码执行逻辑详解 Hey there! Let's break down how the UpsertDocumentAsync API works in Azure Cosmos DB, and walk through exactly what's happening in your code snippet.
核心工作原理
First off, "Upsert" is a portmanteau of Update + Insert—and that's exactly what this API does. Here's the server-side logic Cosmos DB follows when you call it:
- It checks if a document with the same
Idand matching partition key (if your container uses partitioning) already exists in the target container. - If the document exists: Cosmos DB replaces the entire existing document with the one you sent (this is a full replacement, not a partial update—unless you use patch APIs instead).
- If the document doesn't exist: Cosmos DB inserts the new document into the container.
A key point: Upsert is an atomic operation handled entirely on the Cosmos DB server. You don't need to read the document first to use it—this avoids race conditions that could happen if you read, modify, and write from the client (like another process updating the document between your read and write).
Let's Walk Through Your Code
First, let's note a small issue in your code: you're redeclaring the upsertOrder variable with var twice, which will throw a compile error. I'll fix that as we go, but let's break down each step:
Your original code (with the duplicate var fixed):
var response = await client.ReadDocumentAsync(UriFactory.CreateDocumentUri(databaseId, collectionId, "docId"), new RequestOptions { PartitionKey = new PartitionKey("pk") }); var upsertOrder = response.Resource; upsertOrder = new Measurements { Id = "docId", value = 3243 }; // Removed duplicate var upsertOrder.SetPropertyValue("value", 5678); response = await client.UpsertDocumentAsync(collectionUri, upsertOrder, new RequestOptions { PartitionKey = new PartitionKey("pk") });
Step 1: Reading the Existing Document
You call ReadDocumentAsync to fetch the document with Id = "docId" from the partition "pk". This pulls the current state of the document from Cosmos DB into response.Resource, which you assign to upsertOrder.
Step 2: Overwriting the Read Document
Here's the critical part: you immediately create a brand new Measurements object with the same Id and assign it to upsertOrder. This completely discards the document you just read—so that initial ReadDocumentAsync call doesn't actually affect the final Upsert operation at all.
Step 3: Modifying the New Object
You use SetPropertyValue("value", 5678) to update the value field of your new Measurements object. A quick note: if Measurements is your own POCO (Plain Old CLR Object) class, you could just do upsertOrder.value = 5678 instead of using SetPropertyValue—that method is more commonly used with the dynamic Document type from the Cosmos DB SDK.
Step 4: Executing the Upsert
When you call UpsertDocumentAsync, Cosmos DB checks the container for a document with Id = "docId" in partition "pk":
- If that document exists: Cosmos DB replaces the entire existing document with your new
Measurementsobject (all fields will be overwritten to match the new object, not just thevaluefield). - If it doesn't exist: Cosmos DB inserts your new
Measurementsdocument into the partition"pk".
What If You Wanted to Update the Existing Document?
If your goal was to modify the existing document (instead of creating a new one from scratch), you should skip creating a new Measurements object and modify the one you read:
var response = await client.ReadDocumentAsync(UriFactory.CreateDocumentUri(databaseId, collectionId, "docId"), new RequestOptions { PartitionKey = new PartitionKey("pk") }); // Convert the read resource to your POCO type var upsertOrder = (Measurements)response.Resource; // Modify the existing object's properties upsertOrder.value = 5678; // Upsert the updated object response = await client.UpsertDocumentAsync(collectionUri, upsertOrder, new RequestOptions { PartitionKey = new PartitionKey("pk") });
This way, you're preserving the existing fields of the document and only updating the value field (or whatever fields you need).
Key Additional Notes
- Partition Key Matching: Always make sure the partition key you specify in the
RequestOptionsmatches the partition key value of the document. If they don't, Cosmos DB won't find the existing document and will insert a new one—resulting in duplicate documents with the sameIdbut different partition keys (since Cosmos DB's uniqueness is based onId + Partition Key). - Performance: If you don't need the existing document's data, skip the
ReadDocumentAsynccall entirely. Directly construct your object and callUpsertDocumentAsync—this saves a network round trip and improves performance. - Atomicity: As mentioned earlier, Upsert is atomic. Cosmos DB guarantees that either the update or insert completes successfully—no partial writes or inconsistent states.
内容的提问来源于stack exchange,提问作者toto'

