If you are willing to take a dependency on NSB within your EF configuration, you can use a DbContextFactory approach to set the external outbox connection and transaction.
Take a look at this EF Core article: Advanced Performance Topics | Microsoft Learn
The concept will be the same (paraphrasing to your use case):
Register a context factory as a Singleton service:
builder.Services.AddDbContextFactory<SomeDbContext>(
o => o.UseSqlServer() // use this override that doesnt provide a connection or connection string);
Next, write a custom context factory which gets a context from the Singleton factory we registered, and injects the outbox connection into context instances it hands out:
public class OutboxDbContextFactory<TDbContext> : IDbContextFactory<TDbContext>
{
private readonly ISqlStorageSession _storage;
private readonly IDbContextFactory<TDbContext> _innerFactory
public OutboxDbContextFactory(IDbContextFactory<TDbContext> innerFactory, ISqlStorageSession storage)
{
//add null checks
_storage = storage;
_innerFactory = innerFactory;
}
public TDbContext CreateDbContext()
{
//potentially check if storage.Connection / Transaction are null
//if you don't expect all injections to happen within a handler,
//a better approach is to inject IServiceProvider and call _services.GetService<ISqlStorageSession>() and check for null
var context = _innerFactory.CreateDbContext();
context.Database.SetDbConnection(_storage.Connection);
//Use the same underlying ADO.NET transaction
context.Database.UseTransaction(_storage.Transaction);
//Ensure context is flushed before the transaction is committed
_storage.OnSaveChanges((s, token) => context.SaveChangesAsync(token));
return context;
}
}
Once we have our custom context factory, register it as a Scoped service:
builder.Services.AddScoped<OutboxDbContextFactory<SomeDbContext>>();
Finally, arrange for a context to get injected from our Scoped factory:
builder.Services.AddScoped(
sp => sp.GetRequiredService<OutboxDbContextFactory<SomeDbContext>>().CreateDbContext());
As this point, your endpoints automatically get injected with a context instance that has the right outbox connection, without having to know anything about it.