EF Core 关于 DbContext 的一个坑

众所周知,官方推荐用法是通过AddDbContextPool注册DbContext服务(默认为Scoped),然后再依赖注入到各个地方。但是实际上,这个用法有一个非常大的问题:即便通过await,也无法防止Blazor Server重入同一个db实例,造成报错。根据网上的资料,了解到在Blazor Server中AddDbContextPool实际上根本无法在同一个Scoped范围中创建多个db实例,也就是跟AddDbContext的用法几乎无差别。于是就有了更为推荐的配置:使用AddDbContextFactory来绑定工厂模式的dbcontext。

工厂模式很简单粗暴,其实跟直接new一个几乎没有差别,最主要的区别就是工厂模式能帮你直接把配置都填好,基本上就是这样了。由于dbcontext是一个占用了系统/其他软件资源的东西,于是需要在调用完成后释放,那么这里推荐就是用using在某一个作用域中使用后自动释放。大致的代码如下:

首先是注册factory:

builder.Services.AddDbContextFactory<ContactContext>(opt =>
    opt.UseSqlServer(...));

在需要注入和使用的地方:

    public TestService2(AddDbContextFactory<ContactContext> factory)
    {
        _factory = factory;
    }

    public virtual async Task<NewProgramViewModel> HandleAsync()
    {
        
        using var context=_factory.CreateContext())
        {
        ...
        }

    }

就是如此简单粗暴,并不复杂,但是也是一个刚入门会遇到的坑,非常建议收藏一波。

对了,如果要在组件里面使用,可以用如下的方式:

@implements IDisposable
@inject IDbContextFactory<ContactContext> DbFactory
...

@code 
{

    ContactContext? Context;

    public void Dispose()
    {
        Context?.Dispose();
    }

    protected override async Task OnInitializedAsync()
    {
        Context = DbFactory.CreateDbContext();
        ...
    }

}
 

参考资料:https://stackoverflow.com/questions/71324564/blazor-server-and-ef-core-a-second-operation-was-started-on-this-context-instan

发表评论