public async Task WriteAsync(IDictionary <string, object> changes, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (changes == null)
            {
                throw new ArgumentNullException(nameof(changes));
            }

            if (changes.Count == 0)
            {
                return;
            }

            // Ensure Initialization has been run
            await InitializeAsync().ConfigureAwait(false);

            foreach (var change in changes)
            {
                var json = JObject.FromObject(change.Value, _jsonSerializer);

                // Remove etag from JSON object that was copied from IStoreItem.
                // The ETag information is updated as an _etag attribute in the document metadata.
                json.Remove("eTag");

                var documentChange = new DocumentStoreItem
                {
                    Id       = CosmosDbKeyEscape.EscapeKey(change.Key),
                    RealId   = change.Key,
                    Document = json,
                };

                var etag = (change.Value as IStoreItem)?.ETag;
                if (etag == null || etag == "*")
                {
                    // if new item or * then insert or replace unconditionally
                    await _container.UpsertItemAsync(
                        documentChange,
                        new PartitionKey(documentChange.PartitionKey),
                        cancellationToken : cancellationToken)
                    .ConfigureAwait(false);
                }
                else if (etag.Length > 0)
                {
                    // if we have an etag, do opt. concurrency replace
                    await _container.UpsertItemAsync(
                        documentChange,
                        new PartitionKey(documentChange.PartitionKey),
                        new ItemRequestOptions()
                    {
                        IfMatchEtag = etag,
                    },
                        cancellationToken)
                    .ConfigureAwait(false);
                }
                else
                {
                    throw new Exception("etag empty");
                }
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Writes storage items to storage.
        /// </summary>
        /// <param name="changes">The items to write to storage, indexed by key.</param>
        /// <param name="cancellationToken">A cancellation token that can be used by other objects
        /// or threads to receive notice of cancellation.</param>
        /// <returns>A task that represents the work queued to execute.</returns>
        /// <seealso cref="DeleteAsync(string[], CancellationToken)"/>
        /// <seealso cref="ReadAsync(string[], CancellationToken)"/>
        public async Task WriteAsync(IDictionary <string, object> changes, CancellationToken cancellationToken)
        {
            if (changes == null)
            {
                throw new ArgumentNullException(nameof(changes));
            }

            if (changes.Count == 0)
            {
                return;
            }

            // Ensure Initialization has been run
            await InitializeAsync().ConfigureAwait(false);

            foreach (var change in changes)
            {
                var json = JObject.FromObject(change.Value, _jsonSerializer);

                // Remove etag from JSON object that was copied from IStoreItem.
                // The ETag information is updated as an _etag attribute in the document metadata.
                json.Remove("eTag");

                var documentChange = new DocumentStoreItem
                {
                    Id       = CosmosDbKeyEscape.EscapeKey(change.Key),
                    ReadlId  = change.Key,
                    Document = json,
                };

                var etag = (change.Value as IStoreItem)?.ETag;
                if (etag == null || etag == "*")
                {
                    // if new item or * then insert or replace unconditionaly
                    await _client.UpsertDocumentAsync(
                        _collectionLink,
                        documentChange,
                        disableAutomaticIdGeneration : true,
                        cancellationToken : cancellationToken).ConfigureAwait(false);
                }
                else if (etag.Length > 0)
                {
                    // if we have an etag, do opt. concurrency replace
                    var uri = UriFactory.CreateDocumentUri(_databaseId, _collectionId, documentChange.Id);
                    var ac  = new AccessCondition {
                        Condition = etag, Type = AccessConditionType.IfMatch
                    };
                    await _client.ReplaceDocumentAsync(
                        uri,
                        documentChange,
                        new RequestOptions { AccessCondition = ac },
                        cancellationToken : cancellationToken).ConfigureAwait(false);
                }
                else
                {
                    throw new Exception("etag empty");
                }
            }
        }
        /// <summary>
        /// Saves store items to storage.
        /// </summary>
        /// <param name="changes">Map of items to write to storage.</param>
        public async Task Write(IDictionary <string, object> changes)
        {
            if (changes == null)
            {
                throw new ArgumentNullException(nameof(changes), "Please provide a StoreItems with changes to persist.");
            }

            var collectionLink = await GetCollectionLink();

            foreach (var change in changes)
            {
                var json = JObject.FromObject(change.Value, _jsonSerializer);

                // Remove etag from JSON object that was copied from IStoreItem.
                // The ETag information is updated as an _etag attribute in the document metadata.
                json.Remove("eTag");

                var documentChange = new DocumentStoreItem
                {
                    Id       = SanitizeKey(change.Key),
                    ReadlId  = change.Key,
                    Document = json
                };

                string eTag = (change.Value as IStoreItem)?.eTag;
                if (eTag == null || eTag == "*")
                {
                    // if new item or * then insert or replace unconditionaly
                    await _client.UpsertDocumentAsync(collectionLink, documentChange, disableAutomaticIdGeneration : true).ConfigureAwait(false);
                }
                else if (eTag.Length > 0)
                {
                    // if we have an etag, do opt. concurrency replace
                    var uri = UriFactory.CreateDocumentUri(_databaseId, _collectionId, documentChange.Id);
                    var ac  = new AccessCondition {
                        Condition = eTag, Type = AccessConditionType.IfMatch
                    };
                    await _client.ReplaceDocumentAsync(uri, documentChange, new RequestOptions { AccessCondition = ac }).ConfigureAwait(false);
                }
                else
                {
                    throw new Exception("etag empty");
                }
            }
        }
        /// <summary>
        /// Inserts or updates one or more items into the Cosmos DB container.
        /// </summary>
        /// <param name="changes">A dictionary of items to be inserted or updated. The dictionary item key
        /// is used as the ID for the inserted / updated item.</param>
        /// <param name="cancellationToken">A <see cref="CancellationToken"/>.</param>
        /// <returns>A Task representing the work to be executed.</returns>
        /// <exception cref="ArgumentNullException">Exception thrown if the changes dictionary is null.</exception>
        /// <exception cref="Exception">Exception thrown is the etag is empty on any of the items within the changes dictionary.</exception>
        public async Task WriteAsync(IDictionary <string, object> changes, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (changes == null)
            {
                throw new ArgumentNullException(nameof(changes));
            }

            if (changes.Count == 0)
            {
                return;
            }

            // Ensure Initialization has been run
            await InitializeAsync().ConfigureAwait(false);

            foreach (var change in changes)
            {
                var json = JObject.FromObject(change.Value, _jsonSerializer);

                // Remove etag from JSON object that was copied from IStoreItem.
                // The ETag information is updated as an _etag attribute in the document metadata.
                json.Remove("eTag");

                var documentChange = new DocumentStoreItem
                {
                    Id       = CosmosDbKeyEscape.EscapeKey(change.Key, _cosmosDbStorageOptions.KeySuffix, _cosmosDbStorageOptions.CompatibilityMode),
                    RealId   = change.Key,
                    Document = json,
                };

                var etag = (change.Value as IStoreItem)?.ETag;

                try
                {
                    if (etag == null || etag == "*")
                    {
                        // if new item or * then insert or replace unconditionally
                        await _container.UpsertItemAsync(
                            documentChange,
                            GetPartitionKey(documentChange.PartitionKey),
                            cancellationToken : cancellationToken)
                        .ConfigureAwait(false);
                    }
                    else if (etag.Length > 0)
                    {
                        // if we have an etag, do opt. concurrency replace
                        await _container.UpsertItemAsync(
                            documentChange,
                            GetPartitionKey(documentChange.PartitionKey),
                            new ItemRequestOptions()
                        {
                            IfMatchEtag = etag,
                        },
                            cancellationToken)
                        .ConfigureAwait(false);
                    }
                    else
                    {
                        throw new ArgumentException("etag empty");
                    }
                }
                catch (CosmosException ex)
                {
                    // This check could potentially be performed before even attempting to upsert the item
                    // so that a request wouldn't be made to Cosmos if it's expected to fail.
                    // However, performing the check here ensures that this custom exception is only thrown
                    // if Cosmos returns an error first.
                    // This way, the nesting limit is not imposed on the Bot Framework side
                    // and no exception will be thrown if the limit is eventually changed on the Cosmos side.
                    if (IsNestingError(json, out var message))
                    {
                        throw new InvalidOperationException(message, ex);
                    }

                    throw;
                }
            }
        }