public async Task <IReadOnlyCollection <ICommittedDomainEvent> > CommitEventsAsync(IIdentity id, IReadOnlyCollection <SerializedEvent> serializedEvents, CancellationToken cancellationToken) { var eventFlowEvents = serializedEvents .Select(e => new EventFlowEvent { AggregateSequenceNumber = e.AggregateSequenceNumber, Metadata = e.SerializedMetadata, AggregateId = id.Value, Data = e.SerializedData }) .ToList(); var expectedVersion = serializedEvents.Min(e => e.AggregateSequenceNumber) - 1; var streamsDbMessages = serializedEvents .Select(e => { var eventId = e.Metadata.EventId.Value; var eventType = string.Format("{0}.{1}.{2}", e.Metadata[MetadataKeys.AggregateName], e.Metadata.EventName, e.Metadata.EventVersion); var data = Encoding.UTF8.GetBytes(e.SerializedData); var header = Encoding.UTF8.GetBytes(e.SerializedMetadata); return(new MessageInput { ID = eventId, Type = eventType, Header = header, Value = data }); }) .ToList(); try { await _client.DB().AppendStream(id.Value, ConcurrencyCheck.ExpectStreamVersion(expectedVersion), streamsDbMessages).ConfigureAwait(false); } catch (OperationCanceledException e) { throw new OptimisticConcurrencyException(e.Message, e); } return(eventFlowEvents); }
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}'"); }