如何处理数据加载器/GraphQL 嵌套查询中的并发 DbContext 访问? [英] How to handle concurrent DbContext access in dataloaders / GraphQL nested queries?

查看:23
本文介绍了如何处理数据加载器/GraphQL 嵌套查询中的并发 DbContext 访问?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用了几个数据加载器,它们使用注入的查询服务(反过来又依赖于 DbContext).它看起来像这样:

I'm using a couple of dataloaders that use injected query services (which in turn have dependencies on a DbContext). It looks something like this:

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);
  });

当我运行一个同时使用两个数据加载器的嵌套查询时,我得到一个异常在上一个异步操作完成之前在这个上下文上启动了第二个操作" 因为两个数据加载器在同时.

When I run a nested query that concurrently uses both dataloaders I get an exception "A second operation started on this context before a previous asynchronous operation completed" because both dataloaders are using the same DbContext at the same time.

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

What's the best way to allow concurrent database access within the query without having to carefully manage DbContexts with ServiceLifeTime.Transient? Or can dataloader expose a way to know when to dispose of transient DbContexts?

推荐答案

从Scoped"切换到Transient"无法解决该问题,因为 Gql.Net 字段解析器是并行执行的.

The problem will not be solved with switching from "Scoped" to "Transient" because Gql.Net field resolvers execute in parallel.

根据您的示例,我希望您的 DbContext 被构造函数注入到您的数据库服务"类(userQueryServicegroupQueryService>),而那些是构造函数注入到您的示例 GraphType 类中的.因此,您的每个数据库服务都具有 DbContext 的完全相同的范围副本.

Based on your example, I'm expecting that your DbContext is constructor-injected into your "db service" classes (userQueryService and groupQueryService), and those were constructor-injected into your example GraphType class. So each of your db services have the exact same, scoped copy of your DbContext.

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

The solution is to lazy-resolve your DbContext.

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

You'd change your db-services to inject an IServiceScopeFactory. You then use that in your loader methods (MyUserFunc and MyGroupFunc) to create a scope and then resolve your DbContext. The problem with this approach ("Service Locator") is that the dependency on your DbContext is hidden inside of your class.

在 CodeReview.StackExchange 上使用这段相对简单的代码 来代替 IServiceScopeFactory<.您无需执行服务定位器"即可获得延迟解析;您的强类型依赖项在构造函数中声明.

Use this relatively simple bit of code here on CodeReview.StackExchange to instead use an IServiceScopeFactory<T>. You get the lazy-resolving without doing "Service Locator"; your strongly-typed dependencies are declared up in the constructor.

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

So pretend your userQueryService variable's class is like so:

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):

Change it to this (again, using the 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 的每次使用都使用它们自己的作用域,因此它们将无法执行像你现在这样的问题.

Now, when Gql.Net's resolvers (well, in this case, the data loader) ultimately execute this method, each use of your DbContext use their own scope and so they won't have execution problems like you have now.

这篇关于如何处理数据加载器/GraphQL 嵌套查询中的并发 DbContext 访问?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆