Beispiel #1
0
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            DocumentMapping.Map(modelBuilder.Entity <Document>());
            DocumentTypeMapping.Map(modelBuilder.Entity <DocumentType>());
        }
        /// <inheritdoc />
        public async Task UpdateDocumentAsync <TDocument>(
            TDocument document,
            DocumentTypeMapping <TDocument> mapping,
            OperationOptions options)
        {
            if (document == null)
            {
                throw new ArgumentNullException(nameof(document));
            }
            if (mapping == null)
            {
                throw new ArgumentNullException(nameof(mapping));
            }

            options = options ?? new OperationOptions();

            var documentId       = mapping.IdMapper(document);
            var existingDocument = await GetDocumentByIdAsync(documentId, mapping);

            if (existingDocument == null)
            {
                throw new NebulaStoreException($"Document '{documentId}' for update does not exist");
            }

            var createdTime  = existingDocument.CreatedTimestamp;
            var modifiedTime = DateTime.UtcNow;

            var dbRecord = CreateDbDocument(document, documentId, mapping, options, createdTime, modifiedTime);

            await UpdateDocumentAsync(dbRecord, existingDocument);
        }
Beispiel #3
0
        public List <IQueryable <VersionedDocumentStoreClient.VersionedDbDocument> > CreateQueryByIds <TDocument>(
            ICollection <string> ids,
            DocumentTypeMapping <TDocument> mapping)
        {
            if (ids == null)
            {
                throw new ArgumentNullException(nameof(ids));
            }
            if (mapping == null)
            {
                throw new ArgumentNullException(nameof(mapping));
            }

            var batchSize = DbAccess.QueryPolicy.GetIdSearchLimit(ids);
            var batched   = ids.Batch(batchSize);

            var result = new List <IQueryable <VersionedDocumentStoreClient.VersionedDbDocument> >();

            foreach (var batch in batched)
            {
                var query = CreateQueryByIdsImpl(batch.ToArray(), mapping);
                result.Add(query);
            }

            return(result);
        }
        /// <inheritdoc />
        public async Task <IList <DocumentWithMetadata <TDocument> > > GetDocumentsAsync <TDocument>(
            DocumentTypeMapping <TDocument> mapping)
        {
            if (mapping == null)
            {
                throw new ArgumentNullException(nameof(mapping));
            }

            var query     = QueryClient.CreateQueryAll(mapping);
            var documents = await ExecuteQueryAsync(query);

            var result = new List <DocumentWithMetadata <TDocument> >();

            foreach (var document in documents)
            {
                var docWithMetaData = ReadDocumentWithMetadata(mapping, document);

                if (docWithMetaData != null)
                {
                    result.Add(docWithMetaData);
                }
            }

            return(result);
        }
            public LargeDocumentStore(IDocumentDbAccessProvider dbAccessProvider) : base(dbAccessProvider, false)
            {
                var config = new DocumentStoreConfigBuilder("LargeStore");

                var largeDocumentType = config.AddDocument("LargeDocument");

                var attachmentDocumentType = config.AddDocument("AttachmentDocument");
                var attachmentType         = attachmentDocumentType.AddAttachment("Attachment");

                _largeMapping = config.AddDocumentMapping <LargeDocument>(largeDocumentType.Name)
                                .SetIdMapper(x => x.Id.ToString())
                                .SetPartitionMapper(x => x.Id.ToString())
                                .Finish();

                _smallMapping = config.AddDocumentMapping <SmallDocumentWithLargeAttachment>(attachmentDocumentType.Name)
                                .SetIdMapper(x => x.Id.ToString())
                                .SetPartitionMapper(x => x.Id.ToString())
                                .Finish();

                _attachmentMapping = _smallMapping.AddAttachmentMapping <LargeDocument>(attachmentType.Name)
                                     .Finish();

                _config = config.Finish();
                _client = CreateStoreLogic(DbAccess, _config);

                DbAccess.ConfigRegistry.RegisterStoreConfigSource(this);
            }
        /// <inheritdoc />
        public async Task <DocumentReadResult <TDocument> > GetDocumentAsync <TDocument>(
            string id,
            DocumentTypeMapping <TDocument> mapping)
        {
            if (id == null)
            {
                throw new ArgumentNullException(nameof(id));
            }
            if (mapping == null)
            {
                throw new ArgumentNullException(nameof(mapping));
            }

            var query     = QueryClient.CreateQueryById(id, mapping);
            var documents = await ExecuteQueryAsync(query);

            var document = documents.FirstOrDefault();

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

            return(ReadDocument(mapping, document));
        }
        /// <inheritdoc />
        public async Task UpsertDocumentAsync <TDocument>(
            TDocument document,
            DocumentTypeMapping <TDocument> mapping,
            OperationOptions options)
        {
            if (document == null)
            {
                throw new ArgumentNullException(nameof(document));
            }
            if (mapping == null)
            {
                throw new ArgumentNullException(nameof(mapping));
            }

            options = options ?? new OperationOptions();

            var documentId       = mapping.IdMapper(document);
            var existingDocument = await GetDocumentByIdAsync(documentId, mapping);

            var modifiedTime = DateTime.UtcNow;
            var createdTime  = existingDocument?.CreatedTimestamp ?? modifiedTime;

            var dbRecord = CreateDbDocument(document, documentId, mapping, options, createdTime, modifiedTime);

            await UpsertDocumentAsync(dbRecord);
        }
        private UnversionedDbDocument CreateDbDocument <TDocument>(
            TDocument document,
            string documentId,
            DocumentTypeMapping <TDocument> mapping,
            OperationOptions operationOptions,
            DateTime createdTime,
            DateTime modifiedTime)
        {
            var dbRecord = new UnversionedDbDocument();

            dbRecord.Id           = CreateDbRecordId(mapping, documentId);
            dbRecord.DocumentId   = documentId;
            dbRecord.Service      = DbAccess.ConfigManager.ServiceName;
            dbRecord.PartitionKey = mapping.PartitionKeyMapper(document);
            dbRecord.Actor        = GetActorId();

            dbRecord.CreatedTimestamp = createdTime;
            dbRecord.Timestamp        = modifiedTime;

            if (operationOptions.Ttl.HasValue)
            {
                dbRecord.TimeToLive = operationOptions.Ttl.Value;
            }

            SetDocumentContent(dbRecord, document, mapping);

            return(dbRecord);
        }
        private VersionedDocumentReadResult <TDocument> ReadDocumentAsync <TDocument>(
            string id,
            DocumentTypeMapping <TDocument> mapping,
            IList <VersionedDbDocument> documents,
            VersionedDocumentReadOptions readOptions)
        {
            VersionedDbDocument document;
            DateTime            createdTime;
            DateTime            modifiedTime;

            if (!TryGetLatestDocument(documents, readOptions, out document, out createdTime, out modifiedTime))
            {
                return(null);
            }

            var metadata = new VersionedDocumentMetadata(document.Version, document.Deleted, createdTime, modifiedTime, document.Actor);

            TDocument content;
            DocumentReadFailureDetails failure;

            if (!TryGetDocumentContent(document, mapping, out content, out failure))
            {
                return(VersionedDocumentReadResult <TDocument> .CreateFailure(id, metadata, failure));
            }

            return(VersionedDocumentReadResult <TDocument> .CreateOkay(id, metadata, content));
        }
        /// <inheritdoc />
        public async Task <VersionedDocumentQueryResult <TDocument> > GetDocumentsAsync <TDocument>(
            string query,
            IEnumerable <DbParameter> parameters,
            DocumentTypeMapping <TDocument> mapping,
            VersionedDocumentReadOptions options)
        {
            if (query == null)
            {
                throw new ArgumentNullException(nameof(query));
            }
            if (mapping == null)
            {
                throw new ArgumentNullException(nameof(mapping));
            }

            parameters = parameters ?? new DbParameter[0];

            var builtQuery = QueryClient.CreateQueryByLatest(mapping, query, parameters);
            var documents  = await ExecuteQueryAsync(builtQuery);

            if (documents.Count == 0)
            {
                return(VersionedDocumentQueryResult <TDocument> .Empty);
            }

            options = options ?? new VersionedDocumentReadOptions();

            List <VersionedDocumentReadResult <TDocument> > loaded = new List <VersionedDocumentReadResult <TDocument> >();
            List <VersionedDocumentReadResult <TDocument> > failed = new List <VersionedDocumentReadResult <TDocument> >();

            foreach (var docs in documents.GroupBy(x => x.DocumentId))
            {
                VersionedDbDocument doc;
                DateTime            createdTime;
                DateTime            modifiedTime;

                if (!TryGetLatestDocument(docs, options, out doc, out createdTime, out modifiedTime))
                {
                    // Document exists but the latest version is deleted.
                    continue;
                }

                var metadata = new VersionedDocumentMetadata(doc.Version, doc.Deleted, createdTime, modifiedTime, doc.Actor);

                TDocument content;
                DocumentReadFailureDetails failure;

                if (TryGetDocumentContent(doc, mapping, out content, out failure))
                {
                    loaded.Add(VersionedDocumentReadResult <TDocument> .CreateOkay(doc.DocumentId, metadata, content));
                }
                else
                {
                    failed.Add(VersionedDocumentReadResult <TDocument> .CreateFailure(doc.DocumentId, metadata, failure));
                }
            }

            return(new VersionedDocumentQueryResult <TDocument>(ImmutableList.CreateRange(loaded), ImmutableList.CreateRange(failed)));
        }
        private async Task <UnversionedDbDocument> GetDocumentByIdAsync <TDocument>(
            string documentId,
            DocumentTypeMapping <TDocument> mapping)
        {
            var query             = QueryClient.CreateQueryById(documentId, mapping);
            var existingDocuments = await ExecuteQueryAsync(query);

            return(existingDocuments.FirstOrDefault());
        }
        /// <inheritdoc />
        public async Task <VersionedDocumentUpsertResult <TDocument> > UpsertDocumentAsync <TDocument>(
            TDocument document,
            DocumentTypeMapping <TDocument> mapping,
            OperationOptions operationOptions)
        {
            if (document == null)
            {
                throw new ArgumentNullException(nameof(document));
            }
            if (mapping == null)
            {
                throw new ArgumentNullException(nameof(mapping));
            }

            operationOptions = operationOptions ?? new OperationOptions();

            var documentId = mapping.IdMapper(document);

            // Get the newest document version if one exists.
            var existingDocument = await GetDocumentIncludingDeleted(documentId, mapping);

            if (existingDocument != null && operationOptions.CheckVersion.HasValue && operationOptions.CheckVersion != existingDocument.Version)
            {
                throw new NebulaStoreConcurrencyException("Existing document version does not match the specified check version.");
            }

            var version = CalculateNextVersion(existingDocument);

            var dbRecord = new VersionedDbDocument();

            dbRecord.Id           = CreateRecordId(documentId, version, mapping);
            dbRecord.DocumentId   = documentId;
            dbRecord.Service      = DbAccess.ConfigManager.ServiceName;
            dbRecord.PartitionKey = mapping.PartitionKeyMapper(document);
            dbRecord.Version      = version;
            dbRecord.Actor        = GetActorId();
            dbRecord.Timestamp    = DateTime.UtcNow;

            SetDocumentContent(dbRecord, document, mapping);

            await CreateDocumentAsync(dbRecord, existingDocument);

            var updatedDocument = await GetDocumentAsync(documentId, version, mapping);

            if (updatedDocument == null)
            {
                throw new NebulaStoreException("Failed to retrieve document after successful upsert");
            }

            if (updatedDocument.ResultType == DocumentReadResultType.Failed)
            {
                throw new NebulaStoreException($"Failed to retrieve document: {updatedDocument.FailureDetails.Message}");
            }

            return(new VersionedDocumentUpsertResult <TDocument>(documentId, updatedDocument.Metadata, updatedDocument.Document));
        }
Beispiel #13
0
        public IQueryable <VersionedDocumentStoreClient.VersionedDbDocument> CreateQueryAll <TDocument>(
            DocumentTypeMapping <TDocument> mapping)
        {
            if (mapping == null)
            {
                throw new ArgumentNullException(nameof(mapping));
            }

            return(CreateQueryByLatest(mapping, null));
        }
Beispiel #14
0
        private async Task PurgeDocument <TDocument>(
            VersionedDocumentStoreClient.VersionedDbDocument document,
            DocumentTypeMapping <TDocument> mapping)
        {
            var contentKey = CreateContentKey(mapping);

            await ExecuteStoredProcedureAsync <PurgeDocumentStoredProcedure>(
                document.PartitionKey,
                contentKey, document.DocumentId);
        }
        /// <inheritdoc />
        public async Task DeleteDocumentAsync <TDocument>(string id, DocumentTypeMapping <TDocument> mapping, OperationOptions operationOptions)
        {
            if (id == null)
            {
                throw new ArgumentNullException(nameof(id));
            }
            if (mapping == null)
            {
                throw new ArgumentNullException(nameof(mapping));
            }

            operationOptions = operationOptions ?? new OperationOptions();

            var query     = QueryClient.CreateQueryById(id, mapping);
            var documents = await ExecuteQueryAsync(query);

            var existingDocument = FindLatestDocumentIncludingDeleted(documents);

            if (existingDocument == null)
            {
                // Document not found. Treated similarly to already deleted.
                return;
            }

            if (operationOptions.CheckVersion.HasValue && operationOptions.CheckVersion != existingDocument.Version)
            {
                throw new NebulaStoreConcurrencyException("Existing document version does not match the specified check version");
            }

            // Only perform removal if it is not already deleted.
            if (existingDocument.Deleted)
            {
                // Document already deleted.
                return;
            }

            // Document not deleted. Create deletion record.

            var version = CalculateNextVersion(existingDocument);

            var dbRecord = new VersionedDbDocument();

            dbRecord.Id           = CreateRecordId(id, version, mapping);
            dbRecord.DocumentId   = existingDocument.DocumentId;
            dbRecord.Service      = DbAccess.ConfigManager.ServiceName;
            dbRecord.PartitionKey = existingDocument.PartitionKey;
            dbRecord.Version      = version;
            dbRecord.Deleted      = true;
            dbRecord.Actor        = GetActorId();
            dbRecord.Timestamp    = DateTime.UtcNow;

            SetDocumentContentFromExisting(dbRecord, existingDocument, mapping);

            await CreateDocumentAsync(dbRecord, existingDocument);
        }
        private VersionedDocumentReadResult <TDocument> ReadLatestDocumentAsync <TDocument>(
            string id,
            DocumentTypeMapping <TDocument> mapping,
            IList <VersionedDbDocument> documents,
            VersionedDocumentReadOptions readOptions)
        {
            if (!TryGetLatestDocument(documents, readOptions, out var document, out var createdTime, out var modifiedTime))
            {
                return(null);
            }

            return(CreateReadResult(id, document, mapping, createdTime, modifiedTime));
        }
        /// <inheritdoc />
        public async Task <DocumentBatchReadResult <TDocument> > GetDocumentsAsync <TDocument>(
            IEnumerable <string> ids,
            DocumentTypeMapping <TDocument> mapping)
        {
            if (ids == null)
            {
                throw new ArgumentNullException(nameof(ids));
            }
            if (mapping == null)
            {
                throw new ArgumentNullException(nameof(mapping));
            }

            var idSet = new HashSet <string>(ids);

            if (idSet.Count == 0)
            {
                return(DocumentBatchReadResult <TDocument> .Empty);
            }

            var query     = QueryClient.CreateQueryByIds(idSet, mapping);
            var documents = await ExecuteQueriesAsync(query);

            var loaded = new List <DocumentReadResult <TDocument> >();
            var failed = new List <DocumentReadResult <TDocument> >();

            foreach (var document in documents)
            {
                var readResult = ReadDocument(mapping, document);

                if (readResult.ResultType == DocumentReadResultType.Loaded)
                {
                    loaded.Add(readResult);
                }
                else
                {
                    failed.Add(readResult);
                }

                idSet.Remove(document.DocumentId);
            }

            var missing = ImmutableList.CreateRange(idSet);

            return(new DocumentBatchReadResult <TDocument>(
                       loaded.ToImmutableList(),
                       missing,
                       failed.ToImmutableList()));
        }
Beispiel #18
0
        /// <inheritdoc />
        public async Task PurgeDocumentsAsync <TDocument>(DocumentTypeMapping <TDocument> mapping)
        {
            if (mapping == null)
            {
                throw new ArgumentNullException(nameof(mapping));
            }

            var query     = _queryClient.CreateQueryAll(mapping);
            var documents = await ExecuteQueryAsync(query);

            foreach (var document in documents.GroupBy(x => x.DocumentId))
            {
                await PurgeDocument(document.First(), mapping);
            }
        }
        private DocumentWithMetadata <TDocument> ReadDocumentWithMetadata <TDocument>(
            DocumentTypeMapping <TDocument> mapping,
            UnversionedDbDocument document)
        {
            var metadata = new DocumentMetadata(document.CreatedTimestamp, document.Timestamp, document.Actor);

            TDocument content;

            if (!TryGetDocumentContent(document, mapping, out content, out _))
            {
                return(null);
            }

            return(new DocumentWithMetadata <TDocument>(metadata, content));
        }
        /// <inheritdoc />
        public async Task <DocumentQueryResult <TDocument> > GetDocumentsAsync <TDocument>(
            string query,
            DocumentTypeMapping <TDocument> mapping)
        {
            if (query == null)
            {
                throw new ArgumentNullException(nameof(query));
            }
            if (mapping == null)
            {
                throw new ArgumentNullException(nameof(mapping));
            }

            return(await GetDocumentsAsync(query, null, mapping));
        }
        /// <inheritdoc />
        public async Task <VersionedDocumentQueryResult <TDocument> > GetDocumentsAsync <TDocument>(
            string query,
            IEnumerable <DbParameter> parameters,
            DocumentTypeMapping <TDocument> mapping)
        {
            if (query == null)
            {
                throw new ArgumentNullException(nameof(query));
            }
            if (mapping == null)
            {
                throw new ArgumentNullException(nameof(mapping));
            }

            return(await GetDocumentsAsync(query, parameters, mapping, null));
        }
        private DocumentReadResult <TDocument> ReadDocument <TDocument>(
            DocumentTypeMapping <TDocument> mapping,
            UnversionedDbDocument document)
        {
            var metadata = new DocumentMetadata(document.CreatedTimestamp, document.Timestamp, document.Actor);

            TDocument content;
            DocumentReadFailureDetails failure;

            if (!TryGetDocumentContent(document, mapping, out content, out failure))
            {
                return(DocumentReadResult <TDocument> .CreateFailure(document.DocumentId, metadata, failure));
            }

            return(DocumentReadResult <TDocument> .CreateOkay(document.DocumentId, metadata, content));
        }
Beispiel #23
0
        private IQueryable <VersionedDocumentStoreClient.VersionedDbDocument> CreateQueryByIdsImpl <TDocument>(
            ICollection <string> ids,
            DocumentTypeMapping <TDocument> mapping)
        {
            // Optimise queries for a single id to use EQUALS instead of IN.
            if (ids.Count == 1)
            {
                return(CreateQueryById(ids.First(), mapping));
            }

            var inIds = "'" + string.Join("','", ids) + "'";

            var query = $"[x].{mapping.IdPropertyName} IN ({inIds})";

            return(CreateQueryByLatest(mapping, query));
        }
            public FlowerStore(IDocumentDbAccessProvider dbAccessProvider) : base(dbAccessProvider, false)
            {
                var config = new DocumentStoreConfigBuilder("Flowers");

                var documentType = config.AddDocument("Daisy").Finish();

                _mapping = config.AddDocumentMapping <Daisy>(documentType.DocumentName)
                           .SetIdMapper(x => x.Id.ToString())
                           .SetPartitionMapper(x => x.Id.ToString())
                           .Finish();

                _config = config.Finish();
                _client = CreateStoreLogic(DbAccess, _config);

                DbAccess.ConfigRegistry.RegisterStoreConfigSource(this);
            }
        /// <inheritdoc />
        public async Task <VersionedDocumentQueryResult <TDocument> > GetDocumentsAsync <TDocument>(
            string query,
            DocumentTypeMapping <TDocument> mapping,
            VersionedDocumentReadOptions options)
        {
            if (query == null)
            {
                throw new ArgumentNullException(nameof(query));
            }
            if (mapping == null)
            {
                throw new ArgumentNullException(nameof(mapping));
            }

            return(await GetDocumentsAsync(query, new DbParameter[0], mapping, options));
        }
Beispiel #26
0
        public OrderStore(
            IDocumentDbAccessProvider dbAccessProvider,
            IDocumentMetadataSource metadataSource) : base(dbAccessProvider)
        {
            var config = new DocumentStoreConfigBuilder("Orders");

            var orderDocumentType = config.AddDocument("Order").Finish();

            _orderMapping = config.AddDocumentMapping <Order>(orderDocumentType.DocumentName)
                            .SetIdMapper(x => x.Id.ToString())
                            .SetPartitionMapper(x => x.Id.ToString())
                            .Finish();

            _config = config.Finish();
            _client = CreateStoreLogic(DbAccess, _config, metadataSource);
        }
        private VersionedDocumentReadResult <TDocument> ReadDocumentVersionAsync <TDocument>(
            string id,
            DocumentTypeMapping <TDocument> mapping,
            IList <VersionedDbDocument> documents)
        {
            var ordered = documents.OrderBy(x => x.Version).ToArray();

            if (ordered.Length == 0 || ordered.Length > 2)
            {
                return(null);
            }

            var firstVersion   = ordered[0];
            var queriedVersion = ordered[ordered.Length - 1];

            return(CreateReadResult(id, queriedVersion, mapping, firstVersion.Timestamp, queriedVersion.Timestamp));
        }
Beispiel #28
0
        /// <inheritdoc />
        public async Task PurgeDocumentAsync <TDocument>(string id, DocumentTypeMapping <TDocument> mapping)
        {
            if (id == null)
            {
                throw new ArgumentNullException(nameof(id));
            }

            var query     = _queryClient.CreateQueryById(id, 1, mapping);
            var documents = await ExecuteQueryAsync(query);

            var document = documents.FirstOrDefault();

            if (document != null)
            {
                await PurgeDocument(document, mapping);
            }
        }
Beispiel #29
0
        public IQueryable <VersionedDocumentStoreClient.VersionedDbDocument> CreateQueryAllVersionsById <TDocument>(
            string id,
            DocumentTypeMapping <TDocument> mapping)
        {
            if (id == null)
            {
                throw new ArgumentNullException(nameof(id));
            }
            if (mapping == null)
            {
                throw new ArgumentNullException(nameof(mapping));
            }

            var idParameter = new DbParameter("id", id);
            var query       = $"[x].{mapping.IdPropertyName} = @id";

            return(CreateQuery(mapping, query, new[] { idParameter }));
        }
        private VersionedDocumentReadResult <TDocument> CreateReadResult <TDocument>(
            string id,
            VersionedDbDocument document,
            DocumentTypeMapping <TDocument> mapping,
            DateTime createdTime,
            DateTime modifiedTime)
        {
            var metadata = new VersionedDocumentMetadata(document.Version, document.Deleted, createdTime, modifiedTime, document.Actor);

            TDocument content;
            DocumentReadFailureDetails failure;

            if (!TryGetDocumentContent(document, mapping, out content, out failure))
            {
                return(VersionedDocumentReadResult <TDocument> .CreateFailure(id, metadata, failure));
            }

            return(VersionedDocumentReadResult <TDocument> .CreateOkay(id, metadata, content));
        }