user4385532
user4385532

Reputation:

How to use a database context in a singleton service?

I would like to store completed chess games in the database to support users watching replays.

So far I have a singleton GameManager that stores all ongoing games. So within startup.cs I have the following line of code:

services.AddSingleton<IBattleManager, BattleManager>();

Now I would like to have BattleManager access the DbContext to save completed games.

public class BattleManager : IBattleManager
{
    //...
    private void EndGame(ulong gameId)
    {
        var DbContext = WhatDoIPutHere?
        lock(gameDictionary[gameId])
        {
            DbContext.Replays.Add(new ReplayModel(gameDictionary[gameId]));
            gameDictionary.Remove(gameId)
        }
    }
}

Is it possible to anyhow achieve this? How?

Failed attempts:

public class BattleManager : IBattleManager
{
    Data.ApplicationDbContext _context;
    public BattleManager(Data.ApplicationDbContext context)
    {
        _context = context;
    }
}

This will clearly fail since one cannot inject EF Core DbContext into a Singleton service like that.

I have a vague feeling that I should do something of this kind:

using (var scope = WhatDoIPutHere.CreateScope())
{
    var DbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
    DbContext.Replays.Add(new ReplayModel(...));
}

Is this the right direction?

Upvotes: 7

Views: 4050

Answers (1)

Shaun Luttin
Shaun Luttin

Reputation: 141512

You're on the right track. The IServiceScopeFactory can do that.

public class BattleManager : IBattleManager {

    private readonly IServiceScopeFactory scopeFactory;

    public BattleManager(IServiceScopeFactory scopeFactory)
    {
        this.scopeFactory = scopeFactory;
    }

    public void MyMethod() {
        using(var scope = scopeFactory.CreateScope()) 
        {
            var db = scope.ServiceProvider.GetRequiredService<DbContext>();

            // when we exit the using block,
            // the IServiceScope will dispose itself 
            // and dispose all of the services that it resolved.
        }
    }
}

The DbContext will behave like it has a Transient scope within that using statement.

Upvotes: 13

Related Questions