首页 > 解决方案 > Entity Framework Core - DBContext 与多个 AJAX 调用发生冲突

问题描述

我有一个 .NET Core 项目,我正在使用 Entity Framework Core 从 EF6 迁移到 .NET 5。虽然迁移大部分进展顺利,但我遇到了在旧 WebForms 版本中运行良好但在 .NET Core 中存在问题的特定页面。

我的项目是一个相当标准的 .NET 5 Web 应用程序,它利用多个“服务”来使用 AJAX 从数据库中获取实时数据。这些服务使用标准 .NET Core DI 依赖注入到控制器中。出于这个问题的目的,我正在尝试按用户名获取用户。我将 AJAX 请求发送到“UserController”,它调用“UserService”,上面有一个“UserRepository”,这是实际 DBContext 所在的位置。我遇到的问题是,在一个特定页面上,它使用四个单独的 AJAX 调用快速连续地从数据库中获取许多用户,这些调用最终都调用相同的 Repository 函数,并导致“在此启动了第二个操作前一个操作完成之前的上下文”错误。我'

我尝试过的事情:

1. 将 DBContext、Repository 和 Service 生命周期设置为“Transient”。

这似乎没有任何区别。默认情况下,我的大多数服务、存储库等都注册为“AddScoped”。我在某处读过,如果一个类被注册为作用域,那么它下的任何类都将具有相同的作用域,因此使它们成为瞬态毫无意义。为此,在使 DBContext 瞬态之后,我尝试也使存储库瞬态,最后是服务,但似乎没有任何区别。这很奇怪,因为我的理解是“瞬态”服务应该在每次调用它们时创建新实例,所以我不确定在这个实例中使用相同的 DBContext 是如何可能的。我可能只是在这里做错了什么。

2.将所有内容设置为“异步任务”并等待

最常见的谷歌搜索这个问题我发现有同样问题的人只是在某个地方错过了“等待”,导致线程冲突。据我所知,堆栈中导致引发错误的代码的所有内容都是异步的,返回带有等待的任务。

3. 使用“使用”块手动为每个请求创建一个新的 DBContext

这只会导致依赖注入的不同问题;“无法访问已处置的对象。对象名称:“IServiceProvider”。我不太清楚这里发生了什么,但我相信这是因为我的 DBContext 的构造函数接受了一个带有连接字符串等的 DbContextOptions,即通过依赖注入传入。似乎在垃圾收集过程中发生了一些事情,即“清理”DBContext构造函数需要获取DbContextOptions的任何服务。如果我改为为DbContext调用无参数构造函数,并且在 DBContext 的“OnConfiguring()”中硬编码连接字符串然后这确实有效,但出于明显的安全原因,我们不允许在代码中硬编码连接字符串。

4. 在“using”块中注入一个“DbContextFactory”来创建新的DbContext,而不是传入选项

这已在其他地方提出,但似乎导致与上述相同的错误;“无法访问已处置的对象。对象名称:“IServiceProvider”。再次,我必须假设这是因为工厂对象仍在接受依赖注入的 DbContextOptions,并且任何获取的内容都被处置得太早了由于某些原因。

感觉就像我在这里遗漏了一些基本的东西,但是之前没有详细使用过 .NET Core 依赖注入,我不确定它到底是什么。显然,我想要发生的是为四个 ajax 调用中的每一个创建一个新的 DBContext,但似乎它们都是共享的。记住我在上面尝试过的事情,我怎样才能让他们使用单独的 DBContexts?

下面是我的代码示例,我无法复制和粘贴完整的示例,所以这不是我的确切代码,但希望能提供一些见解:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient(IUserService, UserService);
    services.AddTransient(IUserRepository, UserRepository);

    services.AddDbContext<MyDbContext(options => options.UseSqlService(Configuration.GetConnectionString("MyConnectionString")));


}


public class UserRepository : BaseRepository, IUserRepository
{
    public MyDbContext Context;

    public UserRepository(MyDbContext context)
    {
        Context = context;
    }

    public async Task<DbUser> GetUserByUsername(string userName)
    {
        return await Context.DbUsers.FirstOrDefault(u => u.UserName == userName);
    }

}

public class UserService : IUserService
{

    private readonly IUserRepository _userRepository;

    public UserService(IUserRepository userRepository /* Other repositories and services go here */)
    {
        _userRepository = userRepository;
    }


    public async Task<SimpleUser> GetUser(string userName)
    {
        SimpleUser user = new SimpleUser();
        DbUser dbUser = await _userRepository.GetUserByUsername(userName);

        if(dbUser == null)
            //Code to either fetch user a different way or create a new one        

        user = new SimpleUser { UserName = dbUser.UserName, DisplayName = dbUser.FullName;};

        return user;
 
    }
}

标签: c#ajaxentity-frameworkasp.net-coreentity-framework-core

解决方案


推荐阅读