Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/Persistence/EfCoreTests/end_to_end_efcore_persistence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,37 @@ public void outbox_for_db_context_mapped()
.DbContext.ShouldBeSameAs(context);
}

[Fact]
public async Task persisting_against_mapped_dbcontext_does_not_start_an_explicit_transaction()
{
await Host.ResetResourceState();

var envelope = new Envelope
{
Data = [1, 2, 3, 4],
OwnerId = 5,
Destination = TransportConstants.RepliesUri,
MessageType = "foo",
ContentType = EnvelopeConstants.JsonContentType,
Status = EnvelopeStatus.Scheduled,
ScheduledTime = DateTimeOffset.UtcNow.AddMinutes(1)
};

using var nested = Host.Services.CreateScope();
var messaging = nested.ServiceProvider.GetRequiredService<IDbContextOutbox<SampleMappedDbContext>>()
.ShouldBeOfType<DbContextOutbox<SampleMappedDbContext>>();

// Regression for #3121 -- scheduling/incoming persistence against a Wolverine-mapped
// DbContext must NOT begin an explicit EF Core transaction. SaveChanges provides its
// own implicit transaction, and the outgoing path already behaves this way.
await messaging.Transaction!.PersistIncomingAsync(envelope);
messaging.DbContext.Database.CurrentTransaction.ShouldBeNull();

// Symmetry check: the outgoing path likewise leaves the transaction alone
await messaging.Transaction!.PersistOutgoingAsync(envelope);
messaging.DbContext.Database.CurrentTransaction.ShouldBeNull();
}

[Fact]
public async Task persist_an_outgoing_envelope_raw()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,17 +98,27 @@ public async Task PersistOutgoingAsync(Envelope[] envelopes)

public async Task PersistIncomingAsync(Envelope envelope)
{
if (DbContext.Database.CurrentTransaction == null)
{
await DbContext.Database.BeginTransactionAsync();
}

if (DbContext.IsWolverineEnabled())
{
// For a Wolverine-mapped DbContext the envelope is just tracked and flushed by
// SaveChangesAsync, so don't force an explicit transaction here. We defer to whatever
// the ambient transaction mode established: in Eager middleware mode a transaction was
// already begun (EnrollDbContextInTransaction / StartDatabaseTransactionForDbContext) and
// the tracked entity enrolls into it at SaveChanges; in Lightweight/lazy mode there is no
// transaction and SaveChanges provides its own implicit one. This mirrors
// PersistOutgoingAsync and keeps ScheduleAsync from starting a transaction the caller
// never asked for (see #3121).
DbContext.Add(new IncomingMessage(envelope));
}
else
{
// The raw branch issues an ADO command that has to share the caller's transaction, so
// here we do need an explicit one when none is already in flight.
if (DbContext.Database.CurrentTransaction == null)
{
await DbContext.Database.BeginTransactionAsync();
}

var conn = DbContext.Database.GetDbConnection();
var tx = DbContext.Database.CurrentTransaction!.GetDbTransaction();
var builder = _database.ToCommandBuilder();
Expand Down
Loading