private async Task SaveChanges <T>( EventStoreDbContext context, Guid sourceId, CancellationToken cancellationToken) where T : class, IEventSourced { try { await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } catch (DbUpdateException exception) when(exception.InnerException is UpdateException) { var updateException = (UpdateException)exception.InnerException; Correlation correlation = updateException .StateEntries .Select(s => s.Entity) .OfType <Correlation>() .FirstOrDefault(); if (correlation != null) { throw new DuplicateCorrelationException( typeof(T), sourceId, correlation.CorrelationId, exception); } throw; } }
private async Task PublishEvents( Guid sourceId, CancellationToken cancellationToken) { using (EventStoreDbContext context = _dbContextFactory.Invoke()) { List <PendingEvent> pendingEvents = await context .PendingEvents .Where(e => e.AggregateId == sourceId) .OrderBy(e => e.Version) .ToListAsync(cancellationToken) .ConfigureAwait(false); List <Envelope> envelopes = pendingEvents .Select(e => RestoreEnvelope(e)) .ToList(); if (envelopes.Any() == false) { return; } await _messageBus.SendBatch(envelopes, cancellationToken).ConfigureAwait(false); context.PendingEvents.RemoveRange(pendingEvents); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } }
private async Task SaveChanges <T>( EventStoreDbContext context, Guid sourceId, Guid?correlationId, CancellationToken cancellationToken) where T : class, IEventSourced { try { using (IDbContextTransaction transaction = await context.Database.BeginTransactionAsync(cancellationToken).ConfigureAwait(false)) { await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); transaction.Commit(); } } catch (DbUpdateException exception) when(correlationId.HasValue) { IQueryable <Correlation> query = from c in context.Correlations where c.AggregateId == sourceId && c.CorrelationId == correlationId select c; if (await query.AnyAsync().ConfigureAwait(false)) { throw new DuplicateCorrelationException( typeof(T), sourceId, correlationId.Value, exception); } throw; } }
private static async Task RemoveEvents( EventStoreDbContext context, List <PendingEvent> pendingEvents, CancellationToken cancellationToken) { foreach (PendingEvent pendingEvent in pendingEvents) { try { context.PendingEvents.Remove(pendingEvent); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } catch (DbUpdateConcurrencyException) { context.Entry(pendingEvent).State = EntityState.Detached; } } }
public async Task FlushPendingEvents_absorbs_exception_caused_by_that_some_pending_event_already_deleted_since_loaded() { // Arrange var completionSource = new TaskCompletionSource <bool>(); IMessageBus messageBus = new AwaitingMessageBus(completionSource.Task); var fixture = new Fixture(); FakeUser user = fixture.Create <FakeUser>(); user.ChangeUsername(fixture.Create(nameof(user.Username))); var eventStore = new SqlEventStore(CreateDbContext, _serializer); await eventStore.SaveEvents <FakeUser>(user.FlushPendingEvents()); var sut = new SqlEventPublisher(CreateDbContext, _serializer, messageBus); // Act Func <Task> action = async() => { Task flushTask = sut.FlushPendingEvents(user.Id, CancellationToken.None); using (EventStoreDbContext db = CreateDbContext()) { List <PendingEvent> pendingEvents = await db .PendingEvents .Where(e => e.AggregateId == user.Id) .OrderBy(e => e.Version) .Take(1) .ToListAsync(); db.PendingEvents.RemoveRange(pendingEvents); await db.SaveChangesAsync(); } completionSource.SetResult(true); await flushTask; }; // Assert action.ShouldNotThrow(); using (EventStoreDbContext db = CreateDbContext()) { (await db.PendingEvents.AnyAsync(e => e.AggregateId == user.Id)) .Should().BeFalse("all pending events should be deleted"); } }
private static async Task RemoveEvents( EventStoreDbContext context, List <PendingEvent> pendingEvents, CancellationToken cancellationToken) { foreach (PendingEvent pendingEvent in pendingEvents) { try { context.PendingEvents.Remove(pendingEvent); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } #if NETSTANDARD2_0 catch (DbUpdateConcurrencyException) #else catch (DbUpdateConcurrencyException exception) when(exception.InnerException is OptimisticConcurrencyException) #endif { context.Entry(pendingEvent).State = EntityState.Detached; } } }
private async Task SaveChanges <T>( EventStoreDbContext context, Guid sourceId, Guid?correlationId, CancellationToken cancellationToken) where T : class, IEventSourced { #if NETSTANDARD2_0 try { using (IDbContextTransaction transaction = await context.Database.BeginTransactionAsync(cancellationToken).ConfigureAwait(false)) { await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); transaction.Commit(); } } catch (DbUpdateException exception) when(correlationId.HasValue) { IQueryable <Correlation> query = from c in context.Correlations where c.AggregateId == sourceId && c.CorrelationId == correlationId select c; if (await query.AnyAsync().ConfigureAwait(false)) { throw new DuplicateCorrelationException( typeof(T), sourceId, correlationId.Value, exception); } throw; } #else try { await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } catch (DbUpdateException exception) when(exception.InnerException is UpdateException) { var updateException = (UpdateException)exception.InnerException; Correlation correlation = updateException .StateEntries .Select(s => s.Entity) .OfType <Correlation>() .FirstOrDefault(); if (correlation != null) { throw new DuplicateCorrelationException( typeof(T), sourceId, correlation.CorrelationId, exception); } throw; } #endif }