在 ASP.NET Core SignalR 中管理 DbContext 生命周期

Managing DbContext lifetime in ASP.NET Core SignalR

我已经实现了一个 ASP.Core SignalR 应用程序。

共享集线器 class 每 10 秒向其所有客户端调用一个信号 来自 class SharedHub(这个 class 不是继承自 Hub 它有逻辑让 IHubContext 调用)

public void Tick(){
    var time = _context.table.time;
    invoke('tick', time.tick);
}

同样在class一旦一个新的连接建立了一个调用更新数据库的方法

public void UpdateSocketConnection(int connectionId){
    var connection =_context.connection;
    connection.id = connectionId;
    _context.saveChanges();
}

此实现的问题在于连接当前正在调用 Tick() 方法并且同时连接了一个客户端。 _context 抛出错误提示:

_context in use.

(我会在重现后更新确切的错误消息)。

我做了什么?

我已经实现了一个工厂方法来在每个方法

之上获取_context的新实例
public void Tick(){
    var time = factory.GetContext().time;
    invoke('tick', time.tick);
}

public void UpdateSocketConnection(int connectionId){
    var context = Factory.getContext();

    var connection =context.connection;
    connection.id = connectionId;
    context .saveChanges();
}

这确实解决了问题。但这似乎不是正确的做法。我不确定每次在每种方法之上获取新上下文时的性能。这似乎是不好的做法。

我想知道这个场景的可能实现方式是什么。

在第一种方法中,DbContext 同时在多个操作之间共享,这会导致错误和意外结果。为了避免在第二种方法中每次都创建和处理 DbContextDbContextPooling 可以提高性能。

可以创建可重用实例池。它没有释放实例,而是 returns 到池中并将实例重置为默认状态。因此,代码不会每次都创建一个新实例,而是首先检查池中是否有可用实例。

您可以在 startup 中的 Configure 方法中启用 DbContextPooling class:

services.AddDbContextPool<YourContext>(options => options.UseSqlServer(connection));

默认池大小值为 128。阅读 this article 了解更多信息。