private async Task UpdateReadModelAsync(
            ReadModelDescription readModelDescription,
            ReadModelUpdate readModelUpdate,
            IReadModelContext readModelContext,
            Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken, Task <ReadModelEnvelope <TReadModel> > > updateReadModel,
            CancellationToken cancellationToken)
        {
            var response = await _elasticClient.GetAsync <TReadModel>(
                readModelUpdate.ReadModelId,
                d => d
                .RequestConfiguration(c => c
                                      .AllowedStatusCodes((int)HttpStatusCode.NotFound))
                .Index(readModelDescription.IndexName.Value),
                cancellationToken)
                           .ConfigureAwait(false);

            var readModelEnvelope = response.Found
                ? ReadModelEnvelope <TReadModel> .With(readModelUpdate.ReadModelId, response.Source, response.Version)
                : ReadModelEnvelope <TReadModel> .Empty(readModelUpdate.ReadModelId);

            readModelEnvelope = await updateReadModel(
                readModelContext,
                readModelUpdate.DomainEvents,
                readModelEnvelope,
                cancellationToken)
                                .ConfigureAwait(false);

            if (readModelContext.IsMarkedForDeletion)
            {
                await DeleteAsync(readModelUpdate.ReadModelId, cancellationToken);

                return;
            }

            try
            {
                await _elasticClient.IndexAsync(
                    readModelEnvelope.ReadModel,
                    d =>
                {
                    d = d
                        .RequestConfiguration(c => c)
                        .Id(readModelUpdate.ReadModelId)
                        .Index(readModelDescription.IndexName.Value);
                    d = response.Found
                            ? d.VersionType(VersionType.ExternalGte).Version(readModelEnvelope.Version.GetValueOrDefault())
                            : d.OpType(OpType.Create);
                    return(d);
                },
                    cancellationToken)
                .ConfigureAwait(false);
            }
            catch (ElasticsearchClientException e)
                when(e.Response?.HttpStatusCode == (int)HttpStatusCode.Conflict)
                {
                    throw new OptimisticConcurrencyException(
                              $"Read model '{readModelUpdate.ReadModelId}' updated by another",
                              e);
                }
        }
Exemplo n.º 2
0
        private async Task UpdateReadModelAsync(ReadModelDescription readModelDescription,
                                                ReadModelUpdate readModelUpdate,
                                                IReadModelContextFactory readModelContextFactory,
                                                Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken,
                                                      Task <ReadModelUpdateResult <TReadModel> > > updateReadModel,
                                                CancellationToken cancellationToken)
        {
            var collection = _mongoDatabase.GetCollection <TReadModel>(readModelDescription.RootCollectionName.Value);
            var filter     = Builders <TReadModel> .Filter.Eq(readmodel => readmodel.Id, readModelUpdate.ReadModelId);

            var result = collection.Find(filter).FirstOrDefault();

            var isNew = result == null;

            var readModelEnvelope = !isNew
                ? ReadModelEnvelope <TReadModel> .With(readModelUpdate.ReadModelId, result)
                : ReadModelEnvelope <TReadModel> .Empty(readModelUpdate.ReadModelId);

            var readModelContext      = readModelContextFactory.Create(readModelUpdate.ReadModelId, isNew);
            var readModelUpdateResult =
                await updateReadModel(readModelContext, readModelUpdate.DomainEvents, readModelEnvelope,
                                      cancellationToken).ConfigureAwait(false);

            if (!readModelUpdateResult.IsModified)
            {
                return;
            }

            if (readModelContext.IsMarkedForDeletion)
            {
                await DeleteAsync(readModelUpdate.ReadModelId, cancellationToken);

                return;
            }

            readModelEnvelope = readModelUpdateResult.Envelope;
            var originalVersion = readModelEnvelope.ReadModel.Version;

            readModelEnvelope.ReadModel.Version = readModelEnvelope.Version;
            try
            {
                await collection.ReplaceOneAsync <TReadModel>(
                    x => x.Id == readModelUpdate.ReadModelId && x.Version == originalVersion,
                    readModelEnvelope.ReadModel,
                    new UpdateOptions()
                {
                    IsUpsert = true
                },
                    cancellationToken);
            }
            catch (MongoWriteException e)
            {
                throw new OptimisticConcurrencyException(
                          $"Read model '{readModelUpdate.ReadModelId}' updated by another",
                          e);
            }
        }
Exemplo n.º 3
0
        private async Task UpdateReadModelAsync(ReadModelDescription readModelDescription,
                                                ReadModelUpdate readModelUpdate,
                                                IReadModelContextFactory readModelContextFactory,
                                                Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken, Task <ReadModelUpdateResult <TReadModel> > > updateReadModel,
                                                CancellationToken cancellationToken)
        {
            var collection = _mongoDatabase.GetCollection <TReadModel>(readModelDescription.RootCollectionName.Value);
            var filter     = Builders <TReadModel> .Filter.Eq(readmodel => readmodel._id, readModelUpdate.ReadModelId);

            var result = collection.Find(filter).FirstOrDefault();

            var isNew = result == null;

            var readModelEnvelope = !isNew
                    ? ReadModelEnvelope <TReadModel> .With(readModelUpdate.ReadModelId, result)
                    : ReadModelEnvelope <TReadModel> .Empty(readModelUpdate.ReadModelId);

            var readModelContext      = readModelContextFactory.Create(readModelUpdate.ReadModelId, isNew);
            var readModelUpdateResult = await updateReadModel(readModelContext, readModelUpdate.DomainEvents, readModelEnvelope, cancellationToken).ConfigureAwait(false);

            if (!readModelUpdateResult.IsModified)
            {
                return;
            }


            readModelEnvelope = readModelUpdateResult.Envelope;
            readModelEnvelope.ReadModel._version = readModelEnvelope.Version;

            await collection.ReplaceOneAsync <TReadModel>(
                x => x._id == readModelUpdate.ReadModelId,
                readModelEnvelope.ReadModel,
                new UpdateOptions()
            {
                IsUpsert = true
            },
                cancellationToken);
        }
        private async Task UpdateReadModelAsync(TDbContext dbContext, IReadModelContextFactory readModelContextFactory,
                                                Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken, Task <ReadModelUpdateResult <TReadModel> > > updateReadModel,
                                                CancellationToken cancellationToken,
                                                ReadModelUpdate readModelUpdate)
        {
            var readModelId       = readModelUpdate.ReadModelId;
            var readModelEnvelope = await GetAsync(dbContext, readModelId, cancellationToken, true)
                                    .ConfigureAwait(false);

            var entity = readModelEnvelope.ReadModel;
            var isNew  = entity == null;

            if (entity == null)
            {
                entity = await _readModelFactory.CreateAsync(readModelId, cancellationToken).ConfigureAwait(false);

                readModelEnvelope = ReadModelEnvelope <TReadModel> .With(readModelId, entity);
            }

            var readModelContext = readModelContextFactory.Create(readModelId, isNew);
            var originalVersion  = readModelEnvelope.Version;
            var updateResult     = await updateReadModel(
                readModelContext,
                readModelUpdate.DomainEvents,
                readModelEnvelope,
                cancellationToken)
                                   .ConfigureAwait(false);

            if (!updateResult.IsModified)
            {
                return;
            }

            if (readModelContext.IsMarkedForDeletion)
            {
                await DeleteAsync(dbContext, readModelId, cancellationToken).ConfigureAwait(false);

                return;
            }

            readModelEnvelope = updateResult.Envelope;
            entity            = readModelEnvelope.ReadModel;

            var descriptor = GetDescriptor(dbContext);
            var entry      = isNew
                ? dbContext.Add(entity)
                : dbContext.Entry(entity);

            descriptor.SetId(entry, readModelId);
            descriptor.SetVersion(entry, originalVersion, readModelEnvelope.Version);
            try
            {
                await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
            }
            catch (DbUpdateConcurrencyException e)
            {
                var databaseValues = await entry.GetDatabaseValuesAsync(cancellationToken)
                                     .ConfigureAwait(false);

                entry.CurrentValues.SetValues(databaseValues);
                throw new OptimisticConcurrencyException(e.Message, e);
            }

            Log.Verbose(() => $"Updated Entity Framework read model {typeof(TReadModel).PrettyPrint()} with ID '{readModelId}' to version '{readModelEnvelope.Version}'");
        }
Exemplo n.º 5
0
        private async Task UpdateReadModelAsync(
            IReadModelContextFactory readModelContextFactory,
            Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken, Task <ReadModelUpdateResult <TReadModel> > > updateReadModel,
            CancellationToken cancellationToken,
            ReadModelUpdate readModelUpdate)
        {
            IMssqlReadModel mssqlReadModel;

            var readModelId       = readModelUpdate.ReadModelId;
            var readModelEnvelope = await GetAsync(readModelId, cancellationToken).ConfigureAwait(false);

            var readModel = readModelEnvelope.ReadModel;
            var isNew     = readModel == null;

            if (readModel == null)
            {
                readModel = await _readModelFactory.CreateAsync(readModelId, cancellationToken)
                            .ConfigureAwait(false);

                mssqlReadModel = readModel as IMssqlReadModel;
                if (mssqlReadModel != null)
                {
                    mssqlReadModel.AggregateId = readModelId;
                    mssqlReadModel.CreateTime  = readModelUpdate.DomainEvents.First().Timestamp;
                }

                readModelEnvelope = ReadModelEnvelope <TReadModel> .With(readModelUpdate.ReadModelId, readModel);
            }

            var readModelContext = readModelContextFactory.Create(readModelId, isNew);

            var originalVersion       = readModelEnvelope.Version;
            var readModelUpdateResult = await updateReadModel(
                readModelContext,
                readModelUpdate.DomainEvents,
                readModelEnvelope,
                cancellationToken)
                                        .ConfigureAwait(false);

            if (!readModelUpdateResult.IsModified)
            {
                return;
            }

            readModelEnvelope = readModelUpdateResult.Envelope;
            if (readModelContext.IsMarkedForDeletion)
            {
                await DeleteAsync(readModelId, cancellationToken).ConfigureAwait(false);

                return;
            }

            mssqlReadModel = readModel as IMssqlReadModel;
            if (mssqlReadModel != null)
            {
                mssqlReadModel.UpdatedTime = DateTimeOffset.Now;
                mssqlReadModel.LastAggregateSequenceNumber = (int)readModelEnvelope.Version.GetValueOrDefault();
            }
            else
            {
                SetVersion(readModel, (int?)readModelEnvelope.Version);
            }

            var sql = isNew
                ? _readModelSqlGenerator.CreateInsertSql <TReadModel>()
                : _readModelSqlGenerator.CreateUpdateSql <TReadModel>();

            var dynamicParameters = new DynamicParameters(readModel);

            if (originalVersion.HasValue)
            {
                dynamicParameters.Add("_PREVIOUS_VERSION", (int)originalVersion.Value);
            }

            var rowsAffected = await _connection.ExecuteAsync(
                Label.Named("mssql-store-read-model", ReadModelNameLowerCase),
                cancellationToken,
                sql,
                dynamicParameters).ConfigureAwait(false);

            if (rowsAffected != 1)
            {
                throw new OptimisticConcurrencyException(
                          $"Read model '{readModelEnvelope.ReadModelId}' updated by another");
            }

            Log.Verbose(() => $"Updated MSSQL read model {typeof(TReadModel).PrettyPrint()} with ID '{readModelId}' to version '{readModelEnvelope.Version}'");
        }
Exemplo n.º 6
0
        private async Task UpdateReadModelAsync(
            IReadModelContext readModelContext,
            Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken, Task <ReadModelEnvelope <TReadModel> > > updateReadModel,
            CancellationToken cancellationToken,
            ReadModelUpdate readModelUpdate)
        {
            var readModelNameLowerCased = typeof(TReadModel).Name.ToLowerInvariant();
            var readModelEnvelope       = await GetAsync(readModelUpdate.ReadModelId, cancellationToken).ConfigureAwait(false);

            var readModel = readModelEnvelope.ReadModel;
            var isNew     = readModel == null;

            if (readModel == null)
            {
                readModel = await _readModelFactory.CreateAsync(readModelUpdate.ReadModelId, cancellationToken).ConfigureAwait(false);

                readModelEnvelope = ReadModelEnvelope <TReadModel> .With(readModelUpdate.ReadModelId, readModel);
            }

            var originalVersion = readModelEnvelope.Version;

            readModelEnvelope = await updateReadModel(
                readModelContext,
                readModelUpdate.DomainEvents,
                readModelEnvelope,
                cancellationToken)
                                .ConfigureAwait(false);

            if (readModelContext.IsMarkedForDeletion)
            {
                await DeleteAsync(readModelUpdate.ReadModelId, cancellationToken);

                return;
            }

            SetVersion(readModel, (int?)readModelEnvelope.Version);
            SetIdentity(readModel, readModelEnvelope.ReadModelId);

            var sql = isNew
                ? _readModelSqlGenerator.CreateInsertSql <TReadModel>()
                : _readModelSqlGenerator.CreateUpdateSql <TReadModel>();

            var dynamicParameters = new DynamicParameters(readModel);

            if (originalVersion.HasValue)
            {
                dynamicParameters.Add("_PREVIOUS_VERSION", (int)originalVersion.Value);
            }

            var rowsAffected = await _connection.ExecuteAsync(
                Label.Named("sql-store-read-model", readModelNameLowerCased),
                cancellationToken,
                sql,
                dynamicParameters)
                               .ConfigureAwait(false);

            if (rowsAffected != 1)
            {
                throw new OptimisticConcurrencyException(
                          $"Read model '{readModelEnvelope.ReadModelId}' updated by another");
            }

            Log.Verbose(() => $"Updated SQL read model {typeof(TReadModel).PrettyPrint()} with ID '{readModelUpdate.ReadModelId}' to version '{readModelEnvelope.Version}'");
        }
Exemplo n.º 7
0
        private async Task UpdateReadModelAsync(
            IReadModelContextFactory readModelContextFactory,
            Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken, Task <ReadModelUpdateResult <TReadModel> > > updateReadModel,
            CancellationToken cancellationToken,
            ReadModelUpdate readModelUpdate)
        {
            var readModelId       = readModelUpdate.ReadModelId;
            var readModelEnvelope = await GetAsync(readModelId, cancellationToken).ConfigureAwait(false);

            var readModel = readModelEnvelope.ReadModel;
            var isNew     = readModel == null;

            if (readModel == null)
            {
                readModel = await _readModelFactory.CreateAsync(readModelId, cancellationToken).ConfigureAwait(false);

                readModelEnvelope = ReadModelEnvelope <TReadModel> .With(readModelUpdate.ReadModelId, readModel);
            }

            var readModelContext = readModelContextFactory.Create(readModelId, isNew);

            var originalVersion = readModelEnvelope.Version;

            var readModelUpdateResult = await updateReadModel(
                readModelContext,
                readModelUpdate.DomainEvents,
                readModelEnvelope,
                cancellationToken)
                                        .ConfigureAwait(false);

            if (!readModelUpdateResult.IsModified)
            {
                return;
            }

            readModelEnvelope = readModelUpdateResult.Envelope;

            if (readModelContext.IsMarkedForDeletion)
            {
                await DeleteAsync(readModelId, cancellationToken).ConfigureAwait(false);

                return;
            }

            var readModelType = typeof(TReadModel);

            var stream = readModelId == "null"
               ? readModelType.Name.ToLowerInvariant()
               : $"{readModelType.Name.ToLowerInvariant()}-{readModelId}";

            var messageInput = new MessageInput
            {
                ID    = Guid.NewGuid().ToString(),
                Type  = $"{readModelType.Name}.{readModelEnvelope.Version}",
                Value = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(readModelEnvelope.ReadModel))
            };

            try
            {
                if (readModelUpdate is CursorBasedReadModelUpdate cursorBasedReadModelUpdate)
                {
                    var cursorsMessageInput = new MessageInput
                    {
                        ID    = Guid.NewGuid().ToString(),
                        Type  = "cursors",
                        Value = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(cursorBasedReadModelUpdate.Cursors))
                    };

                    await _client.DB().AppendStreams(
                        new StreamInput(cursorBasedReadModelUpdate.CursorsStream, new List <MessageInput> {
                        cursorsMessageInput
                    }),
                        new StreamInput(stream, ConcurrencyCheck.ExpectStreamVersion(originalVersion.GetValueOrDefault()), new List <MessageInput> {
                        messageInput
                    })
                        ).ConfigureAwait(false);
                }
                else
                {
                    await _client.DB().AppendStream(stream, ConcurrencyCheck.ExpectStreamVersion(originalVersion.GetValueOrDefault()), messageInput).ConfigureAwait(false);
                }
            }
            catch (Exception e)
            {
                throw new OptimisticConcurrencyException($"Read model '{readModelEnvelope.ReadModelId}' updated by another", e);
            }

            Log.Verbose(() => $"Updated StreamsDB read model {typeof(TReadModel).PrettyPrint()} with ID '{readModelId}' to version '{readModelEnvelope.Version}'");
        }
Exemplo n.º 8
0
        private async Task UpdateReadModelAsync(ReadModelDescription readModelDescription, ReadModelUpdate readModelUpdate,
                                                IReadModelContextFactory readModelContextFactory,
                                                Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken,
                                                      Task <ReadModelUpdateResult <TReadModel> > > updateReadModel, CancellationToken cancellationToken)
        {
            var readModelContext  = readModelContextFactory.Create(readModelUpdate.ReadModelId, true);
            var collection        = _mongoDatabase.GetCollection <TReadModel>(readModelDescription.RootCollectionName.Value);
            var readModelEnvelope = ReadModelEnvelope <TReadModel> .Empty(readModelUpdate.ReadModelId);

            var modelUpdateResult =
                await updateReadModel(readModelContext, readModelUpdate.DomainEvents, readModelEnvelope,
                                      cancellationToken).ConfigureAwait(false);

            readModelEnvelope = modelUpdateResult.Envelope;
            readModelEnvelope.ReadModel._id =
                ObjectIdGenerator.Instance.GenerateId(collection, readModelEnvelope.ReadModel);
            await collection.InsertOneAsync(
                readModelEnvelope.ReadModel,
                new InsertOneOptions { BypassDocumentValidation = true },
                cancellationToken);
        }