Using Efcore's DbContext with Websockets in ASP.NET (Long living DbContext)

Viewed 53

we have a API written in ASP.NET with net6. We use ef core to communicate to a mysql database. Works great for http requests, but not for websockets.

We have a websocket endpoint which can receive messages from a client. Those messages can trigger an action in the controller, which accesses the database. For example a write action, add to a table entity x that I just created in my code. Works great the first time, but due to the db context tracking changes it will render in a error, if I try to add the same entity again (entity was removed in the database by some other service or whatever). This was to be expected, since we still have the same DbContext from the action that was executed before, and it still has the tracked changes.

Error looks like this, your probably familiar with it.

System.InvalidOperationException: The instance of entity type 'Block' cannot be tracked because another instance with the key value '{Blocksowner: 3ae66710-5307-022f-2c31-5bcb1f468af2, Blockspartner: SYSTEM}' is already being tracke
d. When attaching existing entities, ensure that only one entity instance with a given key value is attached.

Issues being, that the DbContext should only be used for a Single-Unit-of-Work, which is fine for requests which dispose the scoped DbContext after the controller disposes. But this obviously doesnt happen, until the websocket connection is closed and the controller disposes. So it uses the same DbContext for everything.

Im kinda stuck here, on how to handle this correctly. My dirty ideas, that I dont like myself, would be:

  • Make the DbContext a Transient, and manually use a using on the retrieved service by DI
  • Same as above, but leave it as a Scoped (Dont actually know if that works)
  • Use a DbContext Factory?
  • Clear the tracked entities every time I execute a different action..

I searched for a solution for this kind of stuff for a long time now, but couldnt find a clear answer on whats the "right way" of doing this.

1 Answers

You can scope the DbContext within the action executed when messages are received, and it will be disposed automatically when leaving the method.

  1. (using new)
    var contextOptions = new DbContextOptionsBuilder<ApplicationDbContext>()
        .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test")
        .Options;

    using (var context = new ApplicationDbContext(contextOptions))
    {
        // action based on message received on socket
    }
  1. (using factory)
    using (var context = _contextFactory.CreateDbContext())
    {
        // action based on message received on socket
    }

See EF Core docs

Note: if you have a very high throughput of messages, you should look into context pooling.

Related