// </CreateItemsAsync> // <ReadItemAsync> private static async Task ReadItemAsync() { Console.WriteLine("\n1.2 - Reading Item by Id"); // Note that Reads require a partition key to be specified. ItemResponse <SalesOrder> response = await container.ReadItemAsync <SalesOrder>( partitionKey : new PartitionKey("Account1"), id : "SalesOrder1"); // Log the diagnostics Console.WriteLine($"Diagnostics for ReadItemAsync: {response.Diagnostics.ToString()}"); // You can measure the throughput consumed by any operation by inspecting the RequestCharge property Console.WriteLine("Item read by Id {0}", response.Resource); Console.WriteLine("Request Units Charge for reading a Item by Id {0}", response.RequestCharge); SalesOrder readOrder = (SalesOrder)response; // Read the same item but as a stream. using (ResponseMessage responseMessage = await container.ReadItemStreamAsync( partitionKey: new PartitionKey("Account1"), id: "SalesOrder1")) { // Item stream operations do not throw exceptions for better performance if (responseMessage.IsSuccessStatusCode) { SalesOrder streamResponse = FromStream <SalesOrder>(responseMessage.Content); Console.WriteLine($"\n1.2.2 - Item Read {streamResponse.Id}"); // Log the diagnostics Console.WriteLine($"\n1.2.2 - Item Read Diagnostics: {responseMessage.Diagnostics.ToString()}"); } else { Console.WriteLine($"Read item from stream failed. Status code: {responseMessage.StatusCode} Message: {responseMessage.ErrorMessage}"); } } }
// </RunBasicOperationsOnDynamicObjects> /// <summary> /// 3. Using ETags to control execution of operations /// 3.1 - Use ETag to control if a ReplaceItem operation should check if ETag of request matches Item /// 3.2 - Use ETag to control if ReadItem should only return a result if the ETag of the request does not match the Item /// </summary> /// <returns></returns> // <UseETags> private static async Task UseETags() { //****************************************************************************************************************** // 3.1 - Use ETag to control if a replace should succeed, or not, based on whether the ETag on the request matches // the current ETag value of the persisted Item // // All items in Cosmos have an _etag field. This gets set on the server every time a item is updated. // // When doing a replace of a item you can opt-in to having the server only apply the Replace if the ETag // on the request matches the ETag of the item on the server. // If someone did an update to the same item since you read it, then the ETag on the server will not match // and the Replace operation can be rejected. //****************************************************************************************************************** Console.WriteLine("\n3.1 - Using optimistic concurrency when doing a ReplaceItemAsync"); //read a item ItemResponse <SalesOrder> itemResponse = await container.ReadItemAsync <SalesOrder>( partitionKey : new PartitionKey("Account1"), id : "SalesOrder1"); Console.WriteLine("ETag of read item - {0}", itemResponse.ETag); SalesOrder item = itemResponse; //Update the total due itemResponse.Resource.TotalDue = 1000000; //persist the change back to the server ItemResponse <SalesOrder> updatedDoc = await container.ReplaceItemAsync <SalesOrder>( partitionKey : new PartitionKey(item.AccountNumber), id : item.Id, item : item); Console.WriteLine("ETag of item now that is has been updated - {0}", updatedDoc.ETag); //now, using the originally retrieved item do another update //but set the AccessCondition class with the ETag of the originally read item and also set the AccessConditionType //this tells the service to only do this operation if ETag on the request matches the current ETag on the item //in our case it won't, because we updated the item and therefore gave it a new ETag try { itemResponse.Resource.TotalDue = 9999999; updatedDoc = await container.ReplaceItemAsync <SalesOrder>(itemResponse, item.Id, new PartitionKey(item.AccountNumber), new ItemRequestOptions { IfMatchEtag = itemResponse.ETag }); } catch (CosmosException cre) { // now notice the failure when attempting the update // this is because the ETag on the server no longer matches the ETag of doc (b/c it was changed in step 2) if (cre.StatusCode == HttpStatusCode.PreconditionFailed) { Console.WriteLine("As expected, we have a pre-condition failure exception\n"); } } //******************************************************************************************************************* // 3.2 - ETag on a ReadItemAsync request can be used to tell the server whether it should return a result, or not // // By setting the ETag on a ReadItemRequest along with an AccessCondition of IfNoneMatch instructs the server // to only return a result if the ETag of the request does not match that of the persisted Item //******************************************************************************************************************* Console.WriteLine("\n3.2 - Using ETag to do a conditional ReadItemAsync"); // Get a item ItemResponse <SalesOrder> response = await container.ReadItemAsync <SalesOrder>(partitionKey : new PartitionKey("Account2"), id : "SalesOrder2"); item = response; Console.WriteLine($"Read doc with StatusCode of {response.StatusCode}"); // Get the item again with conditional access set, no item should be returned response = await container.ReadItemAsync <SalesOrder>( partitionKey : new PartitionKey("Account2"), id : "SalesOrder2", requestOptions : new ItemRequestOptions() { IfNoneMatchEtag = itemResponse.ETag }); Console.WriteLine("Read doc with StatusCode of {0}", response.StatusCode); // Now change something on the item, then do another get and this time we should get the item back response.Resource.TotalDue = 42; response = await container.ReplaceItemAsync <SalesOrder>(item, item.Id, new PartitionKey(item.AccountNumber)); response = await container.ReadItemAsync <SalesOrder>( partitionKey : new PartitionKey("Account2"), id : "SalesOrder2", requestOptions : new ItemRequestOptions() { IfNoneMatchEtag = itemResponse.ETag }); Console.WriteLine("Read doc with StatusCode of {0}", response.StatusCode); }
// </ReadItemAsync> // <QueryItems> private static async Task QueryItems() { //****************************************************************************************************************** // 1.4 - Query for items by a property other than Id // // NOTE: Operations like AsEnumerable(), ToList(), ToArray() will make as many trips to the database // as required to fetch the entire result-set. Even if you set MaxItemCount to a smaller number. // MaxItemCount just controls how many results to fetch each trip. //****************************************************************************************************************** Console.WriteLine("\n1.4 - Querying for a item using its AccountNumber property"); QueryDefinition query = new QueryDefinition( "select * from sales s where s.AccountNumber = @AccountInput ") .WithParameter("@AccountInput", "Account1"); FeedIterator <SalesOrder> resultSet = container.GetItemQueryIterator <SalesOrder>( query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Account1"), MaxItemCount = 1 }); List <SalesOrder> allSalesForAccount1 = new List <SalesOrder>(); while (resultSet.HasMoreResults) { SalesOrder sale = (await resultSet.ReadNextAsync()).First(); Console.WriteLine($"\n1.4.1 Account Number: {sale.AccountNumber}; Id: {sale.Id} "); allSalesForAccount1.Add(sale); } Console.WriteLine($"\n1.4.2 Query found {allSalesForAccount1.Count} items."); // Use the same query as before but get the cosmos response message to access the stream directly FeedIterator streamResultSet = container.GetItemQueryStreamIterator( query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Account1"), MaxItemCount = 10, MaxConcurrency = 1 }); List <SalesOrder> allSalesForAccount1FromStream = new List <SalesOrder>(); while (streamResultSet.HasMoreResults) { using (ResponseMessage responseMessage = await streamResultSet.ReadNextAsync()) { // Item stream operations do not throw exceptions for better performance if (responseMessage.IsSuccessStatusCode) { dynamic streamResponse = FromStream <dynamic>(responseMessage.Content); List <SalesOrder> salesOrders = streamResponse.Documents.ToObject <List <SalesOrder> >(); Console.WriteLine($"\n1.4.3 - Item Query via stream {salesOrders.Count}"); allSalesForAccount1FromStream.AddRange(salesOrders); } else { Console.WriteLine($"Query item from stream failed. Status code: {responseMessage.StatusCode} Message: {responseMessage.ErrorMessage}"); } } } Console.WriteLine($"\n1.4.4 Query found {allSalesForAccount1FromStream.Count} items."); if (allSalesForAccount1.Count != allSalesForAccount1FromStream.Count) { throw new InvalidDataException($"Both query operations should return the same list"); } }
// </ReadItemAsync> // <ReadAllItems> private static async Task ReadAllItems() { //****************************************************************************************************************** // 1.3 - Read all items in a container // // NOTE: Operations like AsEnumerable(), ToList(), ToArray() will make as many trips to the database // as required to fetch the entire result-set. Even if you set MaxItemCount to a smaller number. // MaxItemCount just controls how many results to fetch each trip. //****************************************************************************************************************** Console.WriteLine("\n1.3 - Read all items with query using a specific partition key"); List <SalesOrder> allSalesForAccount1 = new List <SalesOrder>(); using (FeedIterator <SalesOrder> resultSet = container.GetItemQueryIterator <SalesOrder>( queryDefinition: null, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Account1") })) { while (resultSet.HasMoreResults) { FeedResponse <SalesOrder> response = await resultSet.ReadNextAsync(); SalesOrder sale = response.First(); Console.WriteLine($"\n1.3.1 Account Number: {sale.AccountNumber}; Id: {sale.Id};"); if (response.Diagnostics != null) { Console.WriteLine($" Diagnostics {response.Diagnostics.ToString()}"); } allSalesForAccount1.AddRange(response); } } Console.WriteLine($"\n1.3.2 Read all items found {allSalesForAccount1.Count} items."); // Use the same query as before but get the cosmos response message to access the stream directly List <SalesOrder> allSalesForAccount1FromStream = new List <SalesOrder>(); using (FeedIterator streamResultSet = container.GetItemQueryStreamIterator( queryDefinition: null, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Account1") })) { while (streamResultSet.HasMoreResults) { using (ResponseMessage responseMessage = await streamResultSet.ReadNextAsync()) { // Item stream operations do not throw exceptions for better performance if (responseMessage.IsSuccessStatusCode) { dynamic streamResponse = FromStream <dynamic>(responseMessage.Content); List <SalesOrder> salesOrders = streamResponse.Documents.ToObject <List <SalesOrder> >(); Console.WriteLine($"\n1.3.3 - Read all items via stream {salesOrders.Count}"); allSalesForAccount1FromStream.AddRange(salesOrders); } else { Console.WriteLine($"Read all items from stream failed. Status code: {responseMessage.StatusCode} Message: {responseMessage.ErrorMessage}"); } } } Console.WriteLine($"\n1.3.4 Read all items found {allSalesForAccount1FromStream.Count} items."); } if (allSalesForAccount1.Count != allSalesForAccount1FromStream.Count) { throw new InvalidDataException($"Both read all item operations should return the same list"); } }
// </Main> // <RunDemoAsync> private static async Task RunDemoAsync( CosmosClient client, Database database) { //-------------------------------------------------------------------------------------------------- // We need Two Containers, Two Users, and some permissions for this sample, // So let's go ahead and set these up initially //-------------------------------------------------------------------------------------------------- // Get, or Create, two separate Containers Container container1 = await database.CreateContainerAsync( id : "Container1", partitionKeyPath : "/AccountNumber"); Container container2 = await database.CreateContainerAsync( id : "Container2", partitionKeyPath : "/AccountNumber"); // Insert two documents in to col1 SalesOrder salesOrder1 = new SalesOrder() { Id = "order1", AccountNumber = "partitionKey1" }; await container1.CreateItemAsync <SalesOrder>( salesOrder1, new PartitionKey(salesOrder1.AccountNumber)); SalesOrder salesOrder2 = new SalesOrder() { Id = "order2", AccountNumber = "pk2" }; await container1.CreateItemAsync <SalesOrder>( salesOrder2, new PartitionKey(salesOrder2.AccountNumber)); // Create a user User user1 = await database.CreateUserAsync("Thomas Andersen"); // Get an existing user and permission. // This is a client side reference and does no verification against Cosmos DB. user1 = database.GetUser("Thomas Andersen"); // Verify the user exists UserProperties userProperties = await user1.ReadAsync(); //Add the read permission to the user and validate the user can //read only the container it has access to await ValidateReadPermissions( client.Endpoint.OriginalString, database.Id, container1, user1); // Insert one item in to container 2 SalesOrder salesOrder3 = new SalesOrder() { Id = "doc3", AccountNumber = "partitionKey" }; await container2.CreateItemAsync <SalesOrder>( salesOrder3, new PartitionKey(salesOrder3.AccountNumber)); // Create a new user User user2 = await database.CreateUserAsync("Robin Wakefield"); //Add the all permission to the user for a single item and validate the user can //only access the single item await ValidateAllPermissionsForItem( client.Endpoint.OriginalString, database.Id, container2, user2, salesOrder3); // Add read permission to user1 on container 2 so query has multiple results PermissionResponse permissionUser1Container2Response = await user1.CreatePermissionAsync( new PermissionProperties( id : "permissionUser1Container2", permissionMode : PermissionMode.Read, container : container2)); Permission permissionUser1Container2 = permissionUser1Container2Response; PermissionProperties user1Container2Properties = permissionUser1Container2Response; Console.WriteLine(); Console.WriteLine($"Created {permissionUser1Container2.Id} with resource URI: {user1Container2Properties.ResourceUri}"); // Get an existing permission and token permissionUser1Container2 = user1.GetPermission("permissionUser1Container2"); // Get an existing permission properties user1Container2Properties = await permissionUser1Container2.ReadAsync(); Console.WriteLine($"Read existing {permissionUser1Container2.Id} with resource URI: {user1Container2Properties.ResourceUri}"); // All user1's permissions in a List List <PermissionProperties> user1Permissions = new List <PermissionProperties>(); FeedIterator <PermissionProperties> feedIterator = user1.GetPermissionQueryIterator <PermissionProperties>(); while (feedIterator.HasMoreResults) { FeedResponse <PermissionProperties> permissions = await feedIterator.ReadNextAsync(); user1Permissions.AddRange(permissions); } }