public async Task <DomainEventResult> AppendAsync( IDomainEvent @event, CancellationToken token = default) { if ([email protected]()) { return(DomainEventResult.StorageFailed(@event.Id, $"The domain event: [{@event.Print()}] cannot be stored mysql state backend.")); } var domainEventRecord = DomainEventRecordPortAdapter.ToDomainEventRecord(@event, _binarySerializer); var parameters = DbParameterProvider.ReflectionParameters(domainEventRecord); var tables = _options.Tables.DomainEventOptions; var insertDomainEventIndexSql = $"INSERT INTO `{tables.DomainEventIndices}`(`DomainCommandId`,`DomainCommandType`,`DomainCommandVersion`,`AggregateRootId`,`AggregateRootType`,`AggregateRootVersion`,`AggregateRootGeneration`,`DomainEventId`,`DomainEventType`,`DomainEventVersion`,`DomainEventPayloadBytes`,`CreatedTimestamp`) VALUES(@DomainCommandId,@DomainCommandType,@DomainCommandVersion,@AggregateRootId,@AggregateRootType,@AggregateRootVersion,@AggregateRootGeneration,@DomainEventId,@DomainEventType,@DomainEventVersion,@DomainEventPayloadBytes,@CreatedTimestamp)"; var insertDomainEventSql = $"INSERT INTO `{tables.DomainEvents}`(`DomainEventId`,`Payload`) VALUES (@DomainEventId,@Payload)"; var maxNumErrorTries = 3; var maxExecutionTime = TimeSpan.FromSeconds(3); var expectedRows = 2; bool ErrorFilter(Exception exc, int attempt) { if (exc is MySqlException inner && inner.HasDuplicateEntry()) { _logger.LogWarning($"{domainEventRecord.AggregateRootType}: [Ignored]find duplicated domain event from mysql state backend:{inner.Message}."); return(false); } _logger.LogError($"Append domain event has unknown exception: {LogFormatter.PrintException(exc)}."); return(true); } var affectedRows = await AsyncExecutorWithRetries.ExecuteWithRetriesAsync(async attempt => { return(await _db.ExecuteAsync( $"{insertDomainEventIndexSql};{insertDomainEventSql};", command => command.Parameters.AddRange(parameters), token)); }, maxNumErrorTries, ErrorFilter, maxExecutionTime).ConfigureAwait(false); if (affectedRows != expectedRows) { return(DomainEventResult.StorageFailed(@event.Id, $"The affected rows returned MySql state backend is incorrect, expected: {expectedRows}, actual: {affectedRows}.")); } return(DomainEventResult.StorageSucceed(@event.Id)); }
public async Task <IEnumerable <IDomainEvent> > GetEventStreamAsync( string aggregateRootId, long startOffset = 0, long endOffset = long.MaxValue, CancellationToken token = default) { PreConditions.NotNullOrEmpty(aggregateRootId, nameof(aggregateRootId)); PreConditions.Nonnegative(startOffset, nameof(startOffset)); var tables = _options.Tables.DomainEventOptions; var sql = $"SELECT d.`DomainCommandId`,d.`DomainCommandType`,d.`DomainCommandVersion`,d.`AggregateRootId`,d.`AggregateRootType`,d.`AggregateRootVersion`,d.`AggregateRootGeneration`,d.`DomainEventId`,d.`DomainEventType`,d.`DomainEventVersion`,d.`CreatedTimestamp`, p.`Payload` FROM `{tables.DomainEventIndices}` d INNER JOIN `{tables.DomainEvents}` p ON d.`DomainEventId`=p.`DomainEventId` WHERE d.`AggregateRootId`=@AggregateRootId AND d.AggregateRootVersion>=@StartOffset AND d.AggregateRootVersion<@EndOffset"; var records = await _db.ReadAsync <DomainEventRecord>(sql, new { AggregateRootId = aggregateRootId, StartOffset = startOffset, EndOffset = endOffset }, token); if (records.IsEmpty()) { return(null); } var domainEvents = new List <IDomainEvent>(); foreach (var record in records) { var domainEvent = DomainEventRecordPortAdapter.ToDomainEvent(record, _typeResolver, _binarySerializer); if (!domainEvent.IsValid()) { _logger.LogCritical($"Found illegal domain event from mysql state backend: {domainEvent.Print()}."); continue; } domainEvents.Add(domainEvent); } return(domainEvents); }