加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – 使用“一对多”关系和WebApi时的实体框架核心异步/等待死

发布时间:2020-12-15 22:43:41 所属栏目:百科 来源:网络整理
导读:我在使用EF Core提供程序的异步实现时遇到了死锁. 说我有以下型号: public class Player{ public string PlayerId { get; set;} public string Name { get; set;} public ListGame Games { get; set;}}public class Game{ public string GameId { get; set;
我在使用EF Core提供程序的异步实现时遇到了死锁.

说我有以下型号:

public class Player
{
    public string PlayerId { get; set;}

    public string Name { get; set;}

    public List<Game> Games { get; set;}
}

public class Game
{
    public string GameId { get; set; }

    public string PlayerId { get; set; }
    public Player Player { get; set;}
}

现在我想运行以下查询:

ctx.Players
.Include(p => p.Games)
.Where(p => p.PlayerId == "123")
.Select(p => new {
    PlayerId = p.PlayerId,Games = p.Games.ToList()
}).ToListAsync();

当我通过Console Application / XUnit测试运行此代码时,它可以作为例外…
但是,当我通过ASP.Net WebApi运行它时,它陷入僵局,永远不会结束……

我一直使用ConfigureAwait(false)来防止这种情况,但似乎有问题的代码在下面.
我认为它可能在EFCore使用的System.Interactive.Async库下 – 更具体地说它在:https://github.com/Reactive-Extensions/Rx.NET/blob/develop/Ix.NET/Source/System.Interactive.Async/ToAsyncEnumerable.cs#L72
有一个调用“结果”,它实际上阻止了执行线程.

有没有人遇到过这种行为,也许有一些解决方法?

请注意,如果我没有加载“游戏”实体,那么一切也正常……

编辑:添加StackTrace :(查看对ToEnumerable的调用)

未标记21672 5工作线程grpc 0(cq 0)System.Interactive.Async.dll!System.Linq.AsyncEnumerable.ToEnumerable_ Normal
mscorlib.dll!System.Threading.Monitor.Wait(object obj,int millisecondsTimeout,bool exitContext)
mscorlib.dll!System.Threading.Monitor.Wait(object obj,int millisecondsTimeout)
mscorlib.dll!System.Threading.ManualResetEventSlim.Wait(int millisecondsTimeout,System.Threading.CancellationToken cancellationToken)
mscorlib.dll!System.Threading.Tasks.Task.SpinThenBlockingWait(int millisecondsTimeout,System.Threading.CancellationToken cancellationToken)
mscorlib.dll!System.Threading.Tasks.Task.InternalWait(int millisecondsTimeout,System.Threading.CancellationToken cancellationToken)
mscorlib.dll!System.Threading.Tasks.Task.GetResultCore(bool waitCompletionNotification)
mscorlib.dll中!System.Threading.Tasks.Task.Result.get()
System.Interactive.Async.dll!System.Linq.AsyncEnumerable.ToEnumerable_(System.Collections.Generic.IAsyncEnumerable source)
mscorlib.dll!System.Collections.Generic.List.List(System.Collections.Generic.IEnumerable collection)
System.Core.dll!System.Linq.Enumerable.ToList(System.Collections.Generic.IEnumerable source)
[轻量级功能]
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.SelectAsyncEnumerable.SelectAsyncEnumerator.MoveNext(System.Threading.CancellationToken cancellationToken)
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine)
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext,System.Threading.ContextCallback callback,object state,bool preserveSyncCtx)
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext,bool preserveSyncCtx)
mscorlib.dll中!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run()
mscorlib.dll中!System.Runtime.CompilerServices.AsyncMethodBuilderCore.OutputAsyncCausalityEvents.AnonymousMethod__0()
mscorlib.dll中!System.Runtime.CompilerServices.AsyncMethodBuilderCore.ContinuationWrapper.Invoke()
mscorlib.dll中!System.Runtime.CompilerServices.TaskAwaiter.OutputWaitEtwEvents.AnonymousMethod__0()
mscorlib.dll中!System.Runtime.CompilerServices.AsyncMethodBuilderCore.ContinuationWrapper.Invoke()
mscorlib.dll!System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(System.Action action,bool allowInlining,ref System.Threading.Tasks.Task currentTask)
mscorlib.dll中!System.Threading.Tasks.Task.FinishContinuations()
mscorlib.dll中!System.Threading.Tasks.Task.FinishStageThree()
mscorlib.dll!System.Threading.Tasks.Task.TrySetResult(bool result)
mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult(bool result)
NG.Data.Spanner.EF.dll!NG.Data.Spanner.EF.Query.Internal.SpannerAsyncQueryingEnumerable.SpannerAsyncEnumerator.MoveNext(System.Threading.CancellationToken cancellationToken)第55行
[恢复异步方法]
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine)
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext,ref System.Threading.Tasks.Task currentTask)
mscorlib.dll中!System.Threading.Tasks.Task.FinishContinuations()
mscorlib.dll中!System.Threading.Tasks.Task.FinishStageThree()
mscorlib.dll!System.Threading.Tasks.Task.TrySetResult(bool result)
mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult(bool result)
Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.SelectAsyncEnumerable.SelectAsyncEnumerator.MoveNext(System.Threading.CancellationToken cancellationToken)
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine)
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext,ref System.Threading.Tasks.Task currentTask)
mscorlib.dll中!System.Threading.Tasks.Task.FinishContinuations()
mscorlib.dll中!System.Threading.Tasks.Task.FinishStageThree()
mscorlib.dll!System.Threading.Tasks.Task.TrySetResult(bool result)
mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult(bool result)
Microsoft.EntityFrameworkCore.Relational.dll!Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable.AsyncEnumerator.MoveNext(System.Threading.CancellationToken cancellationToken)
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine)
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext,ref System.Threading.Tasks.Task currentTask)
mscorlib.dll中!System.Threading.Tasks.Task.FinishContinuations()
mscorlib.dll中!System.Threading.Tasks.Task.FinishStageThree()
mscorlib.dll!System.Threading.Tasks.Task.TrySetResult(bool result)
mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult(bool result)
Microsoft.EntityFrameworkCore.Relational.dll!Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable.AsyncEnumerator.BufferlessMoveNext(bool buffer,System.Threading.CancellationToken cancellationToken)
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine)
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext,ref System.Threading.Tasks.Task currentTask)
mscorlib.dll中!System.Threading.Tasks.Task.FinishContinuations()
mscorlib.dll中!System.Threading.Tasks.Task.FinishStageThree()
mscorlib.dll!System.Threading.Tasks.Task.TrySetResult(System .__ Canon result)
mscorlib.dll!System.Threading.Tasks.TaskCompletionSource.TrySetResult(System .__ Canon result)
Microsoft.EntityFrameworkCore.Relational.dll!System.Threading.Tasks.TaskExtensions.Cast.AnonymousMethod__0(System.Threading.Tasks.Task t)
mscorlib.dll中!System.Threading.Tasks.ContinuationTaskFromResultTask.InnerInvoke()
mscorlib.dll中!System.Threading.Tasks.Task.Execute()
mscorlib.dll!System.Threading.Tasks.Task.ExecutionContextCallback(object obj)
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext,bool preserveSyncCtx)
mscorlib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot)
mscorlib.dll!System.Threading.Tasks.Task.ExecuteEntry(bool bPreventDoubleExecution)
mscorlib.dll!System.Threading.Tasks.ThreadPoolTask??Scheduler.TryExecuteTaskInline(System.Threading.Tasks.Task task,bool taskWasPreviouslyQueued)
mscorlib.dll!System.Threading.Tasks.TaskScheduler.TryRunInline(System.Threading.Tasks.Task task,bool taskWasPreviouslyQueued)
mscorlib.dll!System.Threading.Tasks.TaskContinuation.InlineIfPossibleOrElseQueue(System.Threading.Tasks.Task任务,bool needsProtection)
mscorlib.dll!System.Threading.Tasks.StandardTaskContinuation.Run(System.Threading.Tasks.Task completedTask,bool bCanInlineContinuationTask)
mscorlib.dll中!System.Threading.Tasks.Task.FinishContinuations()
mscorlib.dll中!System.Threading.Tasks.Task.FinishStageThree()
mscorlib.dll!System.Threading.Tasks.Task.TrySetResult(System .__ Canon result)
mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult(object result)
NG.Data.Spanner.EF.dll!NG.Data.Spanner.EF.Storage.Internal.SpannerRelationalCommand.ExecuteAsync(Microsoft.EntityFrameworkCore.Storage.IRelationalConnection连接,字符串executeMethod,System.Collections.Generic.IReadOnlyDictionary parameterValues,bool closeConnection,System.Threading.CancellationToken cancellationToken)第41行
[恢复异步方法]
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine)
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext,ref System.Threading.Tasks.Task currentTask)
mscorlib.dll中!System.Threading.Tasks.Task.FinishContinuations()
mscorlib.dll中!System.Threading.Tasks.Task.FinishStageThree()
mscorlib.dll!System.Threading.Tasks.Task.TrySetResult(System .__ Canon result)
mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult(object result)
NG.Data.Spanner.EF.dll!NG.Data.Spanner.EF.Storage.Internal.SpannerRelationalCommand.ExecuteAsync(NG.Data.Spanner.EF.Storage.Internal.IOBehavior ioBehavior,Microsoft.EntityFrameworkCore.Storage.IRelationalConnection连接,string executeMethod,System.Threading.CancellationToken cancellationToken)第128行
[恢复异步方法]
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine)
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext,ref System.Threading.Tasks.Task currentTask)
mscorlib.dll中!System.Threading.Tasks.Task.FinishContinuations()
mscorlib.dll中!System.Threading.Tasks.Task.FinishStageThree()
mscorlib.dll!System.Threading.Tasks.Task.TrySetResult(System .__ Canon result)
mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult(System.Data.Common.DbDataReader result)
NG.Data.Spanner.dll!NG.Data.Spanner.SpannerCommand.ExecuteDbDataReaderAsync(System.Data.CommandBehavior behavior,System.Threading.CancellationToken cancellationToken)第67行
[恢复异步方法]
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine)
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext,ref System.Threading.Tasks.Task currentTask)
mscorlib.dll中!System.Threading.Tasks.Task.FinishContinuations()
mscorlib.dll中!System.Threading.Tasks.Task.FinishStageThree()
mscorlib.dll!System.Threading.Tasks.Task.TrySetResult(System .__ Canon result)
mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult(Google.Cloud.Spanner.V1.ResultSet result)
NG.Data.Spanner.dll!NG.Data.Spanner.SpannerConnection.RunQuery(string commandText)第118行
[恢复异步方法]
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine)
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext,ref System.Threading.Tasks.Task currentTask)
mscorlib.dll中!System.Threading.Tasks.Task.FinishContinuations()
mscorlib.dll中!System.Threading.Tasks.Task.FinishStageThree()
mscorlib.dll!System.Threading.Tasks.Task.TrySetResult(System .__ Canon result)
mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult(Google.Cloud.Spanner.V1.ResultSet result)
Google.Api.Gax.Grpc.dll!Google.Api.Gax.Grpc.ApiCallRetryExtensions.WithRetry.AnonymousMethod__0(Google.Cloud.Spanner.V1.ExecuteSqlRequest request,Google.Api.Gax.Grpc.CallSettings callSettings)
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine)
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext,ref System.Threading.Tasks.Task currentTask)
mscorlib.dll中!System.Threading.Tasks.Task.FinishContinuations()
mscorlib.dll中!System.Threading.Tasks.Task.FinishStageThree()
mscorlib.dll!System.Threading.Tasks.Task.TrySetResult(System .__ Canon result)
mscorlib.dll!System.Threading.Tasks.TaskCompletionSource.TrySetResult(System .__ Canon result)
mscorlib.dll!System.Threading.Tasks.TaskCompletionSource.SetResult(System .__ Canon result)
Grpc.Core.dll!Grpc.Core.Internal.AsyncCall.HandleUnaryResponse(bool成功,Grpc.Core.Internal.ClientSideStatus receivedStatus,byte [] receivedMessage,Grpc.Core.Metadata responseHeaders)
Grpc.Core.dll!Grpc.Core.Internal.CallSafeHandle.StartUnary.AnonymousMethod__0(bool success,Grpc.Core.Internal.BatchContextSafeHandle context)
Grpc.Core.dll!Grpc.Core.Internal.CompletionRegistry.HandleBatchCompletion(bool成功,Grpc.Core.Internal.BatchContextSafeHandle ctx,Grpc.Core.Internal.BatchCompletionDelegate回调)
Grpc.Core.dll!Grpc.Core.Internal.CompletionRegistry.RegisterBatchCompletion.AnonymousMethod__0(bool成功)
Grpc.Core.dll!Grpc.Core.Internal.GrpcThreadPool.RunHandlerLoop(Grpc.Core.Internal.CompletionQueueSafeHandle cq,Grpc.Core.Profiling.IProfiler optionalProfiler)
Grpc.Core.dll!Grpc.Core.Internal.GrpcThreadPool.CreateAndStartThread.AnonymousMethod__0()
mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(对象状态)
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext,System.Threading.ContextCallback回调,对象状态)
mscorlib.dll中!System.Threading.ThreadHelper.ThreadStart()

解决方法

经过长时间的调试后,我发现了问题,并做了一些解决它的工作.我会试着解释一下:

我正在为Google Spanner db构建EF Core提供程序.为此,我使用的是Google Spanner DotNet API – 尽管它尚未公开且非常不成熟的API(实际上它只是自动生成的代码).

这个API使用gRPC API,我看到的是在第一次使用此API的异步调用时,gRPC线程(gRPC有自己的线程池实现)被使用并将用于下一次执行调用 – 因为continuation将继续仅从gRPC线程池中的此线程运行.

EFCore为使用System.Interactive.Async lib的“Games”列表调用ToEnumerable.在ToEnumerable实现上,它实际上调用“Result”来阻止执行线程并等待结果.因为使用了gRPC线程并等待执行完成所以我们有一个死锁…

我的解决方法是使用以下代码:

GrpcEnvironment.SetCompletionQueueCount(1);

这样,允许continuation在gRPC线程池的其他线程上运行.

这只是一种解决方法,应该使用.然而,在此期间它有助于解决问题……

真正的解决方案应该是在System.Interactive.Async lib的EFCore用法中,关于ToEnumerable …(它应该是纯异步).

编辑:
如果它可能与任何人相关:
刚刚发布了针对Google Spanner db的EF Core提供商:
https://github.com/NoGame/NG.Data.Spanner

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读