多线程 API 应用程序中的 EF 核心 DbContext

Phi*_*ier 9 c# entity-framework entity-framework-core .net-core asp.net-core

tl;dr即使 DbContext 不是线程安全的,我如何在多线程 .NET Core API 应用程序中使用实体框架?

语境

我正在开发一个 .NET Core API 应用程序,它公开了几个访问数据库并从中读取数据的 RESTful 接口,同时运行多个 TimedHostedServices 作为后台工作线程,定期从其他 Web 服务轮询数据并将它们存储到数据库中。

我知道 DbContext 不是线程安全的。我在 Stackoverflow 上阅读了很多文档、博客文章和答案,我可以找到很多(部分矛盾的)答案,但在使用 DI 时没有真正的“最佳实践”。

我尝试过的事情

由于竞争条件,ServiceLifetime.Scoped通过AddDbContext扩展方法使用默认值会导致异常。

我不想使用锁(例如信号量),因为明显的缺点是:

  • 代码被锁污染,并且 try/catch/finally 安全地释放锁
  • 它看起来并不“健壮”,即当我忘记锁定访问 DbContext 的区域时。
  • 在使用同时处理并发连接和访问的数据库时,人为地同步应用程序中的数据库访问似乎是多余和“不自然的”

不是注入MyDbContextDbContextOptions<MyDbContext>而是仅在我需要访问数据库时构建上下文,using在读/写后使用语句立即处理它似乎有很多资源使用开销和不必要的许多连接打开/关闭。

我真的很纳闷:这怎么能实现呢?

我不认为我的用例是超级特殊的——从后台工作人员填充数据库并从 Web API 层查询它——所以应该有一种有意义的方法来使用 ef core 来做到这一点。

非常感谢!

ESG*_*ESG 8

你应该在任何时候创建一个范围 TimedHostedServices触发。

在构造函数中注入服务提供者:

public MyServiceService(IServiceProvider services)
{
    _services = services;
}
Run Code Online (Sandbox Code Playgroud)

然后在任务触发时创建一个范围

using (var scope = _services.CreateScope())
{
    var anotherService = scope.ServiceProvider.GetRequiredService<AnotherService>();

    anotherService.Something();
}
Run Code Online (Sandbox Code Playgroud)

文档中提供更完整的示例


Fab*_*bio 8

另一种方法是创建自己的 DbContextFactory 并为每个查询实例化新实例。

public class DbContextFactory
{
    public YourDbContext Create()
    {
        var options = new DbContextOptionsBuilder<YourDbContext>()
            .UseSqlServer(_connectionString)
            .Options;

        return new YourDbContext(options);
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

public class Service
{
    private readonly DbContextFactory _dbContextFactory;

    public Service(DbContextFactory dbContextFactory) 
         => _dbContextFactory = dbContextFactory;

    public void Execute()
    {
        using (var context = _dbContextFactory.Create())
        {
            // use context
        }
    }
}    
Run Code Online (Sandbox Code Playgroud)

使用工厂,您无需再担心范围,并使您的代码摆脱 ASP.NET Core 依赖项。
您将能够异步执行查询,这是在没有解决方法的情况下使用作用域 DbContext 不可能实现的。
您始终对调用时保存的数据充满信心.SaveChanges(),在使用作用域 DbContext 的情况下,某些实体可能在其他类中发生更改。