rsivanov / Rebus.SqlServer.Outbox

Provides an implementation of Rebus.Outbox abstraction using MS SQL Server as an outbox storage
MIT License
5 stars 2 forks source link

Question: How to Use this library with EF Core? #4

Open maulik-modi opened 3 years ago

maulik-modi commented 3 years ago

How to Use this library with EF Core

  1. How to configure to use DbContext Connection
_dbContext.Database.GetDbConnection()
 .Outbox(c => 
    {
       TBD  
    }, 

Reference:https://github.com/dotnetcore/CAP x.UseEntityFramework<AppDbContext>();

  1. How to publish Events

    public async Task<long> CreateOrder(CreateOrderRequest createOrderRequest)
        {
            using  var dbTransaction = _catalogContext.Database.BeginTransaction());
            _dbContext.Orders.Add(order);
            _dbContext.SaveChanges();           
            await _bus.Publish(new OrderCreated {Id = order.id, ProductId = createOrderRequest.ProductId, Quantity = createOrderRequest.Quantity});         
    
            dbTransaction.Commit();
            return id;
        }`
rsivanov commented 3 years ago
  1. Look at the example code of SqlConnectionFactory.

Just use

services.AddDbContext<AppDbContext>(options => options.UseSqlServer(SqlConnectionFactory.GetOpenConnection(connectionString)));

2.

using (var transScope = new LocalTransactionScope())
{
  _dbContext.Orders.Add(order);
  _dbContext.SaveChanges();         
  await _bus.Publish(new OrderCreated {Id = order.id, ProductId = createOrderRequest.ProductId, Quantity = createOrderRequest.Quantity});
  transScope.Complete();
}
maulik-modi commented 3 years ago

@rsivanov , Does it mean Every Command needs AT-LEAST TWO database trips to complete processing? We may have some validations as well e.g. UniqueNessCheck, TransactionId....

Did you face any performance penalty?

rsivanov commented 3 years ago

Yes, but it’s not an issue. I didn’t have any performance problems with that approach. If you want to have just one database trip, you can implement Rebus.EFCore.Outbox that will use the same dbcontext to insert a record into the outbox storage table. I personally don’t use EF Core, so I didn’t have a need for that

maulik-modi commented 3 years ago

@rsivanov , The thing is Autogenerated Id from database is ONLY available after SaveChanges; Hence TWO trips are inevitable.

DDD Purists use GUID everywhere so they know Id in advance and can commit in one transaction, some people even do transaction at Mediator Pipeline behavior or Transaction decorator level

maulik-modi commented 3 years ago

@rsivanov , What changes will be required to use DatabaseTransaction instead of LocalTransactionScope ?

rsivanov commented 3 years ago

You need some way to pass a transaction to the outbox storage code. The simplest approach is to use an ambient transaction context, that’s why I use LocalTransactionScope. You could devise your own mechanism for that using static AsyncLocal fields, but I don’t see the point

maulik-modi commented 3 years ago

I would like to use something recommended here https://docs.microsoft.com/en-us/ef/core/saving/transactions

Transaction scope guidance here says - "It is possible to use ambient transactions if you need to coordinate across a larger scope." and this defeats the purpose of having Outbox (table in the same database, sharing same database transaction).

maulik-modi commented 3 years ago

Is it possible to expose GetTransaction method from within SQLConnectionFactory that returns IDbTransaction and use that transaction to perform Db operations to business tables and technical table(Outbox)? https://github.com/hansehe/SafeRebus/blob/master/src/SafeRebus/Implementations/SafeRebus.Outbox.Database/DbProvider.cs

Using external DbTransactions (relational databases only)

rsivanov commented 3 years ago

I would like to use something recommended here https://docs.microsoft.com/en-us/ef/core/saving/transactions

  • Using external DbTransactions (relational databases only)
  • Controlling transactions

Transaction scope guidance here says - "It is possible to use ambient transactions if you need to coordinate across a larger scope." and this defeats the purpose of having Outbox (table in the same database, sharing same database transaction).

LocalTransactionScope guarantees that the same database connection and the same local database transaction will be used for writing to the outbox table as for writing to business domain tables.