【问题标题】:How to use a database context in a singleton service?如何在单例服务中使用数据库上下文?
【发布时间】:2018-08-21 08:31:14
【问题描述】:

我想将完成的棋局存储在数据库中,以支持用户观看回放。

到目前为止,我有一个单例 GameManager 存储所有正在进行的游戏。所以在startup.cs 内我有以下代码行:

services.AddSingleton<IBattleManager, BattleManager>();

现在我想让BattleManager 访问DbContext 来保存已完成的游戏。

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)
        }
    }
}

是否有可能实现这一目标?怎么样?

尝试失败:

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

这显然会失败,因为无法将 EF Core DbContext 注入到这样的 Singleton 服务中。

我有一种模糊的感觉,我应该做这样的事情:

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

这是正确的方向吗?

【问题讨论】:

  • 通常情况下,DbContext 被注册为 Scope。为什么要注册为单身人士?
  • @Win 我知道 DbContext 已注册为 Scope。我不想将其注册为单例。但是,我需要一个 Singleton (GameManager) 将内容写入数据库。有可能吗?我的想法是,我需要以某种方式在 GameManager 的方法中创建范围,以便 DbContext 可以安全地驻留在那里而不会违反其范围。
  • 或者Singletons永远不能访问数据库,句号,句号,因为我们可能不会,不应该以任何方式在Singletons中使用Scoped服务?
  • 也许你应该问问自己为什么你认为你需要一个单身人士IBattleManager。你从中得到什么?为什么它的多个实例不能存在?如果你把它做成 Scope 会破坏什么吗?
  • @Brad 1) BattleManager 需要记住当前正在处理的回合之前的所有回合。我想既然它需要持续记住所有正在进行的游戏,它应该是一个单例。 2) 战斗管理器不是由 razor pages 调用,而是由 SignalR Hub 调用。

标签: asp.net-core singleton entity-framework-core


【解决方案1】:

你在正确的轨道上。 IServiceScopeFactory 可以做到这一点。

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.
        }
    }
}

DbContext 的行为就像它在 using 语句中具有 Transient 范围一样。

【讨论】:

  • 这也是我最终的结果,但很多人说注入服务提供者是一种反模式。我完全确定原因,但这似乎是服务在后台运行并需要与数据库 (DbContext) 对话的常见问题。我想知道是否有任何关于如何处理这种常见场景而不使 DbContext 成为单例并通过注入服务提供者接口来避免反模式的额外指导。
  • 我认为人们过于依赖注入 IServiceProvider 作为反模式。他们的理由是,如果你改变你的 DI 容器,那么你就已经将它换成了容器提供的任何东西。坦率地说,归根结底,您需要解决问题并拥有工作代码并采用被认为是“反模式”的依赖项似乎是最小的担忧。此外,如果您要更换 DI 容器,您可能会在这里和那里进行大量的错误修复,直到它完全正常工作。我说不用担心。我没有。
猜你喜欢
  • 1970-01-01
  • 2022-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-26
相关资源
最近更新 更多