Exemple #1
0
        internal ICosmosDBService GetService(string connectionString, string preferredLocations = "", bool useMultipleWriteLocations = false)
        {
            string           cacheKey         = BuildCacheKey(connectionString, preferredLocations, useMultipleWriteLocations);
            ConnectionPolicy connectionPolicy = CosmosDBUtility.BuildConnectionPolicy(_options.ConnectionMode, _options.Protocol, preferredLocations, useMultipleWriteLocations);

            return(ClientCache.GetOrAdd(cacheKey, (c) => _cosmosDBServiceFactory.CreateService(connectionString, connectionPolicy)));
        }
Exemple #2
0
 private static async Task CreateLeaseCollectionIfNotExistsAsync(ICosmosDBService leaseCosmosDBService, string databaseName, string collectionName, int throughput)
 {
     try
     {
         await CosmosDBUtility.CreateDatabaseAndCollectionIfNotExistAsync(leaseCosmosDBService, databaseName, collectionName, null, throughput);
     }
     catch (DocumentClientException ex) when(ex.Message.Contains(SharedThroughputRequirementException))
     {
         await CosmosDBUtility.CreateDatabaseAndCollectionIfNotExistAsync(leaseCosmosDBService, databaseName, collectionName, LeaseCollectionRequiredPartitionKey, throughput);
     }
 }
        public async Task AddAsync(T item, CancellationToken cancellationToken = default(CancellationToken))
        {
            bool create = false;

            try
            {
                await UpsertDocument(_docDBContext, item);
            }
            catch (Exception ex)
            {
                if (CosmosDBUtility.TryGetDocumentClientException(ex, out DocumentClientException de) &&
                    de.StatusCode == HttpStatusCode.NotFound)
                {
                    if (_docDBContext.ResolvedAttribute.CreateIfNotExists)
                    {
                        create = true;
                    }
                    else
                    {
                        // Throw a custom error so that it's easier to decipher.
                        string message = $"The collection '{_docDBContext.ResolvedAttribute.CollectionName}' (in database '{_docDBContext.ResolvedAttribute.DatabaseName}') does not exist. To automatically create the collection, set '{nameof(CosmosDBAttribute.CreateIfNotExists)}' to 'true'.";
                        throw new InvalidOperationException(message, ex);
                    }
                }
                else
                {
                    throw;
                }
            }

            if (create)
            {
                await CosmosDBUtility.CreateDatabaseAndCollectionIfNotExistAsync(_docDBContext);

                await UpsertDocument(_docDBContext, item);
            }
        }
Exemple #4
0
        public async Task <ITriggerBinding> TryCreateAsync(TriggerBindingProviderContext context)
        {
            // Tries to parse the context parameters and see if it belongs to this [CosmosDBTrigger] binder
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            ParameterInfo            parameter = context.Parameter;
            CosmosDBTriggerAttribute attribute = parameter.GetCustomAttribute <CosmosDBTriggerAttribute>(inherit: false);

            if (attribute == null)
            {
                return(null);
            }

            ConnectionMode?desiredConnectionMode     = _options.ConnectionMode;
            Protocol?      desiredConnectionProtocol = _options.Protocol;

            DocumentCollectionInfo     documentCollectionLocation;
            DocumentCollectionInfo     leaseCollectionLocation;
            ChangeFeedProcessorOptions processorOptions = BuildProcessorOptions(attribute);

            processorOptions.StartFromBeginning = attribute.StartFromBeginning;
            if (attribute.MaxItemsPerInvocation > 0)
            {
                processorOptions.MaxItemCount = attribute.MaxItemsPerInvocation;
            }

            ICosmosDBService monitoredCosmosDBService;
            ICosmosDBService leaseCosmosDBService;

            try
            {
                string triggerConnectionString             = ResolveAttributeConnectionString(attribute);
                CosmosDBConnectionString triggerConnection = new CosmosDBConnectionString(triggerConnectionString);
                if (triggerConnection.ServiceEndpoint == null)
                {
                    throw new InvalidOperationException("The connection string for the monitored collection is in an invalid format, please use AccountEndpoint=XXXXXX;AccountKey=XXXXXX;.");
                }

                string leasesConnectionString             = ResolveAttributeLeasesConnectionString(attribute);
                CosmosDBConnectionString leasesConnection = new CosmosDBConnectionString(leasesConnectionString);
                if (leasesConnection.ServiceEndpoint == null)
                {
                    throw new InvalidOperationException("The connection string for the leases collection is in an invalid format, please use AccountEndpoint=XXXXXX;AccountKey=XXXXXX;.");
                }

                documentCollectionLocation = new DocumentCollectionInfo
                {
                    Uri            = triggerConnection.ServiceEndpoint,
                    MasterKey      = triggerConnection.AuthKey,
                    DatabaseName   = ResolveAttributeValue(attribute.DatabaseName),
                    CollectionName = ResolveAttributeValue(attribute.CollectionName)
                };

                documentCollectionLocation.ConnectionPolicy.UserAgentSuffix = CosmosDBTriggerUserAgentSuffix;

                if (desiredConnectionMode.HasValue)
                {
                    documentCollectionLocation.ConnectionPolicy.ConnectionMode = desiredConnectionMode.Value;
                }

                if (desiredConnectionProtocol.HasValue)
                {
                    documentCollectionLocation.ConnectionPolicy.ConnectionProtocol = desiredConnectionProtocol.Value;
                }

                leaseCollectionLocation = new DocumentCollectionInfo
                {
                    Uri            = leasesConnection.ServiceEndpoint,
                    MasterKey      = leasesConnection.AuthKey,
                    DatabaseName   = ResolveAttributeValue(attribute.LeaseDatabaseName),
                    CollectionName = ResolveAttributeValue(attribute.LeaseCollectionName)
                };

                leaseCollectionLocation.ConnectionPolicy.UserAgentSuffix = CosmosDBTriggerUserAgentSuffix;

                if (desiredConnectionMode.HasValue)
                {
                    leaseCollectionLocation.ConnectionPolicy.ConnectionMode = desiredConnectionMode.Value;
                }

                if (desiredConnectionProtocol.HasValue)
                {
                    leaseCollectionLocation.ConnectionPolicy.ConnectionProtocol = desiredConnectionProtocol.Value;
                }

                string resolvedPreferredLocations = ResolveAttributeValue(attribute.PreferredLocations);
                foreach (var location in CosmosDBUtility.ParsePreferredLocations(resolvedPreferredLocations))
                {
                    documentCollectionLocation.ConnectionPolicy.PreferredLocations.Add(location);
                    leaseCollectionLocation.ConnectionPolicy.PreferredLocations.Add(location);
                }

                if (string.IsNullOrEmpty(documentCollectionLocation.DatabaseName) ||
                    string.IsNullOrEmpty(documentCollectionLocation.CollectionName) ||
                    string.IsNullOrEmpty(leaseCollectionLocation.DatabaseName) ||
                    string.IsNullOrEmpty(leaseCollectionLocation.CollectionName))
                {
                    throw new InvalidOperationException("Cannot establish database and collection values. If you are using environment and configuration values, please ensure these are correctly set.");
                }

                if (documentCollectionLocation.Uri.Equals(leaseCollectionLocation.Uri) &&
                    documentCollectionLocation.DatabaseName.Equals(leaseCollectionLocation.DatabaseName) &&
                    documentCollectionLocation.CollectionName.Equals(leaseCollectionLocation.CollectionName))
                {
                    throw new InvalidOperationException("The monitored collection cannot be the same as the collection storing the leases.");
                }

                monitoredCosmosDBService = _configProvider.GetService(triggerConnectionString, resolvedPreferredLocations);
                leaseCosmosDBService     = _configProvider.GetService(leasesConnectionString, resolvedPreferredLocations);

                if (attribute.CreateLeaseCollectionIfNotExists)
                {
                    await CreateLeaseCollectionIfNotExistsAsync(leaseCosmosDBService, leaseCollectionLocation.DatabaseName, leaseCollectionLocation.CollectionName, attribute.LeasesCollectionThroughput);
                }
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException(string.Format("Cannot create Collection Information for {0} in database {1} with lease {2} in database {3} : {4}", attribute.CollectionName, attribute.DatabaseName, attribute.LeaseCollectionName, attribute.LeaseDatabaseName, ex.Message), ex);
            }

            return(new CosmosDBTriggerBinding(
                       parameter,
                       documentCollectionLocation,
                       leaseCollectionLocation,
                       processorOptions,
                       monitoredCosmosDBService,
                       leaseCosmosDBService,
                       _logger));
        }
Exemple #5
0
        public async Task <ITriggerBinding> TryCreateAsync(TriggerBindingProviderContext context)
        {
            // Tries to parse the context parameters and see if it belongs to this [CosmosDBTrigger] binder
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            ParameterInfo            parameter = context.Parameter;
            CosmosDBTriggerAttribute attribute = parameter.GetCustomAttribute <CosmosDBTriggerAttribute>(inherit: false);

            if (attribute == null)
            {
                return(null);
            }

            _monitorConnectionString = _nameResolver.Resolve(CosmosDBConfiguration.AzureWebJobsCosmosDBConnectionStringName);
            _leasesConnectionString  = _nameResolver.Resolve(CosmosDBConfiguration.AzureWebJobsCosmosDBConnectionStringName);

            DocumentCollectionInfo documentCollectionLocation;
            DocumentCollectionInfo leaseCollectionLocation;
            ChangeFeedHostOptions  leaseHostOptions = ResolveLeaseOptions(attribute);
            int?maxItemCount = null;

            if (attribute.MaxItemsPerInvocation > 0)
            {
                maxItemCount = attribute.MaxItemsPerInvocation;
            }

            try
            {
                string triggerConnectionString             = ResolveAttributeConnectionString(attribute);
                CosmosDBConnectionString triggerConnection = new CosmosDBConnectionString(triggerConnectionString);
                if (triggerConnection.ServiceEndpoint == null)
                {
                    throw new InvalidOperationException("The connection string for the monitored collection is in an invalid format, please use AccountEndpoint=XXXXXX;AccountKey=XXXXXX;.");
                }

                string leasesConnectionString             = ResolveAttributeLeasesConnectionString(attribute, triggerConnectionString);
                CosmosDBConnectionString leasesConnection = new CosmosDBConnectionString(leasesConnectionString);
                if (leasesConnection.ServiceEndpoint == null)
                {
                    throw new InvalidOperationException("The connection string for the leases collection is in an invalid format, please use AccountEndpoint=XXXXXX;AccountKey=XXXXXX;.");
                }

                documentCollectionLocation = new DocumentCollectionInfo
                {
                    Uri            = triggerConnection.ServiceEndpoint,
                    MasterKey      = triggerConnection.AuthKey,
                    DatabaseName   = ResolveAttributeValue(attribute.DatabaseName),
                    CollectionName = ResolveAttributeValue(attribute.CollectionName)
                };

                documentCollectionLocation.ConnectionPolicy.UserAgentSuffix = CosmosDBTriggerUserAgentSuffix;

                leaseCollectionLocation = new DocumentCollectionInfo
                {
                    Uri            = leasesConnection.ServiceEndpoint,
                    MasterKey      = leasesConnection.AuthKey,
                    DatabaseName   = ResolveAttributeValue(attribute.LeaseDatabaseName),
                    CollectionName = ResolveAttributeValue(attribute.LeaseCollectionName)
                };

                leaseCollectionLocation.ConnectionPolicy.UserAgentSuffix = CosmosDBTriggerUserAgentSuffix;

                if (string.IsNullOrEmpty(documentCollectionLocation.DatabaseName) ||
                    string.IsNullOrEmpty(documentCollectionLocation.CollectionName) ||
                    string.IsNullOrEmpty(leaseCollectionLocation.DatabaseName) ||
                    string.IsNullOrEmpty(leaseCollectionLocation.CollectionName))
                {
                    throw new InvalidOperationException("Cannot establish database and collection values. If you are using environment and configuration values, please ensure these are correctly set.");
                }

                if (documentCollectionLocation.Uri.Equals(leaseCollectionLocation.Uri) &&
                    documentCollectionLocation.DatabaseName.Equals(leaseCollectionLocation.DatabaseName) &&
                    documentCollectionLocation.CollectionName.Equals(leaseCollectionLocation.CollectionName))
                {
                    throw new InvalidOperationException("The monitored collection cannot be the same as the collection storing the leases.");
                }

                if (attribute.CreateLeaseCollectionIfNotExists)
                {
                    // Not disposing this because it might be reused on other Trigger since Triggers could share lease collection
                    ICosmosDBService service = _config.GetService(leasesConnectionString);
                    await CosmosDBUtility.CreateDatabaseAndCollectionIfNotExistAsync(service, leaseCollectionLocation.DatabaseName, leaseCollectionLocation.CollectionName, null, attribute.LeasesCollectionThroughput);
                }
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException(string.Format("Cannot create Collection Information for {0} in database {1} with lease {2} in database {3} : {4}", attribute.CollectionName, attribute.DatabaseName, attribute.LeaseCollectionName, attribute.LeaseDatabaseName, ex.Message), ex);
            }

            return(new CosmosDBTriggerBinding(parameter, documentCollectionLocation, leaseCollectionLocation, leaseHostOptions, maxItemCount));
        }