Single transaction, multiple service calls to Entity Framework updating database

David Thielen 2,276 Reputation points
2024-03-22T15:46:31.9333333+00:00

Hi all;

I have a case with a single database defined by a single Entity Framework DbContext.

I have things set up where updating the database is via services. This separates things out nicely and makes it easy to mock the database for the bUnit (Blazor server app) tests.

But I need to accomplish the following:

  1. Start transaction
  2. Call PlaceOrder service
  3. Call UpdateInventory service
  4. Complete transaction

How can I accomplish this in a way where the outer code does not explicitly use Entity Framework (so I can mock in unit tests)?

The best I've come up with is create MyDbContext and pass that in to each. That works but it feels awkward. I am SQL Server (dev) and Azure SQL Database (production) so a solution that requires SQL Server works for me.

thanks - dave

Entity Framework Core
Entity Framework Core
A lightweight, extensible, open-source, and cross-platform version of the Entity Framework data access technology.
697 questions
{count} votes

2 answers

Sort by: Most helpful
  1. hossein jalilian 2,535 Reputation points
    2024-03-23T00:36:15.1+00:00

    Thanks for posting your question in the Microsoft Q&A forum.

    For solving this issue, I have two suggestions:

    1- You can consider using the Saga pattern and implement it within your system. This involves orchestrating the long-lived process or workflow and managing its state transitions.

    2- Alternatively, you can manually implement transactional logic by writing compensating actions for services and operations. If an error occurs at any point in the process, these compensating actions can rollback previous operations to revert the system to its previous state.

    Please don't forget to close up the thread here by upvoting and accept it as an answer if it is helpful


  2. Bruce (SqlWork.com) 56,026 Reputation points
    2024-04-01T20:13:59.5533333+00:00

    as your transactions all require using the same dbcontext instance, I would not use the service model. this is for more loosely coupled code. Unit of work would work, but this is just a wrapper around your current services.

    Because you are using single dbcontext instance, and two services cannot access at the same time. I'd probably pass the context to service calls rather than use a constructor, because the caller needs to orchestrate the access.

    I would probably change the services to MyDbContext static extensions (so they can be in separate file and can use a mocked dbcontext):

    using (var transaction = _dbContext.Database.BeginTransaction())
    {
       _dbContext.PlaceOrder(order);
    
       ....
    
       _dbContext.UpdateInventory(order);
    
       ... 
       transaction.Commit();
    }
    

    note: I think it a bad practice to put this code in a Blazor app. the blazor app should call a webapi to access the database and perform transactions.

    0 comments No comments