entity-framework - 如何处理数据加载器/GraphQL 嵌套查询中的并发 DbContext 访问?

标签 entity-framework entity-framework-core graphql graphql-dotnet dataloader

我正在使用几个使用注入(inject)查询服务的数据加载器(这些服务又依赖于 DbContext)。它看起来像这样:

Field<ListGraphType<UserType>>(
  "Users",
  resolve: context =>
  {
    var loader = accessor.Context.GetOrAddBatchLoader<Guid, IEnumerable<User>>(
      "MyUserLoader",
      userQueryService.MyUserFunc);

    return loader.LoadAsync(context.Source.UserId);
  });
Field<ListGraphType<GroupType>>(
  "Groups",
  resolve: context =>
  {
    var loader = accessor.Context.GetOrAddBatchLoader<Guid, IEnumerable<Group>>(
      "MyGroupLoader",
      groupQueryService.MyGroupFunc);

    return loader.LoadAsync(context.Source.GroupId);
  });

当我运行同时使用两个数据加载器的嵌套查询时,我得到一个异常 "A second operation started on this context before a previous asynchronous operation completed"因为两个数据加载器同时使用相同的 DbContext。

在查询中允许并发数据库访问而无需仔细管理 DbContexts 与 ServiceLifeTime.Transient 的最佳方法是什么? ?或者数据加载器可以公开一种知道何时处理 transient DbContexts 的方法吗?

最佳答案

从“Scoped”切换到“Transient”不会解决问题,因为 Gql.Net 字段解析器是并行执行的。

根据您的示例,我希望您的 DbContext被构造函数注入(inject)到您的“数据库服务”类( userQueryServicegroupQueryService )中,并且这些构造函数被注入(inject)到您的示例 GraphType 类中。因此,您的每个数据库服务都具有您的 DbContext 的完全相同的范围副本。 .

解决方案是延迟解析您的 DbContext .

快速而简单的方法是使用“服务定位器”模式。

您将更改 db-services 以注入(inject) IServiceScopeFactory .然后你在你的加载器方法中使用它( MyUserFuncMyGroupFunc )来创建一个范围,然后解析你的 DbContext .这种方法(“服务定位器”)的问题是依赖于您的 DbContext隐藏在您的类(class)中。

更好的方法(类似,但不是“服务定位器”)......

Use this relatively simple bit of code here on CodeReview.StackExchange改为使用 IServiceScopeFactory<T> .您无需执行“服务定位器”即可获得延迟解析;您的强类型依赖项在构造函数中声明。

例子

所以假装你的userQueryService变量的类是这样的:

MyDbContext _dbContext;
public UserQueryService(MyDbContext dbContext) => _dbContext = dbContext;

public async Task<IDictionary<Guid, IEnumerable<User>> MyUserFunc(IEnumerable<Guid> userIds)
{
    // code that uses _dbContext and returns the data...
}

将其更改为此(再次使用 IServiceScopeFactory<T> ):
IServiceScopeFactory<MyDbContext> _dbFactory;
public UserQueryService(IServiceScopeFactory<MyDbContext> dbFactory) => _dbFactory = dbFactory;

public async Task<IDictionary<Guid, IEnumerable<User>> MyUserFunc(IEnumerable<Guid> userIds)
{
    using var scope = _dbFactory.CreateScope();
    var dbContext = scope.GetRequiredService();
    // code that uses dbContext and returns the data...
}

现在,当 Gql.Net 的解析器(好吧,在本例中为数据加载器)最终执行此方法时,每次使用您的 DbContext使用他们自己的范围,因此他们不会像现在这样遇到执行问题。

关于entity-framework - 如何处理数据加载器/GraphQL 嵌套查询中的并发 DbContext 访问?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58873249/

相关文章:

c# - AutoMapper.Collections.EntityFramework

node.js - 创建源插件时无法将数据保存在 gatsby graphql 层中

c# - Entity Framework 无效操作异常 "An entity object cannot be referenced by multiple instances of IEntityChangeTracker"

c# - 在 Linux 中使用带有 DbContext 的 Entity Framework 有问题

.net - EFCore cli 生成无用的迁移

entity-framework - Entity Framework Core 全局动态查询过滤器

c# - Dotnet 核心 3.1 版本的 RelationalMetadataExtensions.Relations()

graphql - 错误 : Cannot find module 'graphql/validation/rules/KnownArgumentNamesRule'

javascript - 如何使用 Apollo 在 React 中执行 GraphQL 查询 onClick?

entity-framework - MVC 6 (Visual Studio 2015) 中缺少 microsoft.data.entity