我正在使用几个使用注入(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)到您的“数据库服务”类( userQueryService
和 groupQueryService
)中,并且这些构造函数被注入(inject)到您的示例 GraphType 类中。因此,您的每个数据库服务都具有您的 DbContext
的完全相同的范围副本。 .
解决方案是延迟解析您的 DbContext
.
快速而简单的方法是使用“服务定位器”模式。
您将更改 db-services 以注入(inject) IServiceScopeFactory
.然后你在你的加载器方法中使用它( MyUserFunc
和 MyGroupFunc
)来创建一个范围,然后解析你的 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/