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

2023-11-27

我正在使用几个使用注入查询服务的数据加载器(这些服务又依赖于 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。

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


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

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

解决方案是惰性解决你的DbContext.

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

您将更改您的数据库服务以注入IServiceScopeFactory。然后您可以在加载器方法中使用它(MyUserFunc and MyGroupFunc)创建一个范围,然后解决你的DbContext。这种方法(“服务定位器”)的问题在于对您的依赖DbContext隐藏在您的班级内。

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

在 Code Review.Stack Exchange 上使用这段相对简单的代码改为使用IServiceScopeFactory<T>。您无需执行“服务定位器”即可获得延迟解析;您的强类型依赖项在构造函数中声明。

Example

所以假装你的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使用自己的作用域,因此他们不会像您现在那样遇到执行问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何处理数据加载器/GraphQL 嵌套查询中的并发 DbContext 访问? 的相关文章

随机推荐