Example #1
0
        // </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");
            }
        }
Example #4
0
        // </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);
            }
        }