在 WCF 服务上配置 IClientMessageInspector 并使用 async/await 调用方法时,上下文丢失

Context lost when IClientMessageInspector is configured on a WCF service and method is called with async/await

提问人:bkqc 提问时间:10/8/2023 最后编辑:bkqc 更新时间:10/19/2023 访问量:40

问:

TLDR:有谁知道在使用 await/async 调用的服务上配置上下文同步时如何完成。IClientMessageInspectorConfigureAwait(false)

我们有一个 .NET FW4.5+ 项目,其中使用 WcfUtils 生成了一个代理类。在此代理中,有不同方法的异步版本。

此外,在我们的 web.config 中,我们在该部分中有一个自定义(但不受我们控制),该部分又会注册一个自定义 .当调用其方法时,此检查器将在某个时候使用 .BehaviourExtensionElementsystem.serviceModelIClientMessageInspectorAfterReceiveReplyHttpContext.Current.Items[stringKey]

问题是,即使我在执行路径中的任何位置都没有 ConfigureAwait(false),也会返回 null。我一直在尝试记录执行,并且上下文一直存在,直到异步调用,甚至在之后,因为 WcfUtil 代理是在没有 await 的情况下生成的(只是一个返回任务)。HttpContext.Currentbase.Channel.MyMethod

我在谷歌上搜索了很多,但找不到任何关于当有 MessageInspector 时如何完成延续的具体信息,尽管我无法理解为什么它与主调用的同步不同。

编辑:在中间检查员中使用了男人

我创建了一个虚拟的 MessageInspector,用于接收消息并将它们传递给正常配置的消息。这让我意识到,在进入检查器时,使用 await 完成的所有调用都没有上下文:经过身份验证的用户现在是应用程序池用户,并且是 null。我还在执行时打印了堆栈,它看起来像这样。HttpContext.Current

à MyNamespace.InTheMiddleMessageInspector.AfterReceiveReply(Message& reply, Object correlationState) dans D:\Builds\sfpauw14759_1\_work\379\s\Client\AccueilClient\SX00.SXCLT00A.AccueilClient\InTheMiddleMessageInspector.cs:ligne 76
à System.ServiceModel.Dispatcher.ImmutableClientRuntime.AfterReceiveReply(ProxyRpc& rpc)
à System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
à System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
à System.ServiceModel.Channels.ServiceChannelProxy.TaskCreator.<>c__DisplayClass7_0`1.<CreateGenericTask>b__0(IAsyncResult asyncResult)
à System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
à System.Threading.Tasks.TaskFactory`1.<>c__DisplayClass44_0`3.<FromAsyncImpl>b__0(IAsyncResult iar)
à System.Runtime.AsyncResult.Complete(Boolean completedSynchronously)
à System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.FinishSend(IAsyncResult result, Boolean completedSynchronously)
à System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.SendCallback(IAsyncResult result)
à System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
à System.Runtime.AsyncResult.Complete(Boolean completedSynchronously)
à System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelAsyncRequest.OnGetResponse(IAsyncResult result)
à System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
à System.Net.LazyAsyncResult.Complete(IntPtr userToken)
à System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
à System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
à System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
à System.Net.ContextAwareResult.Complete(IntPtr userToken)
à System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
à System.Net.HttpWebRequest.ProcessResponse()
à System.Net.HttpWebRequest.SetResponse(CoreResponseData coreResponseData)
à System.Net.ConnectionReturnResult.SetResponses(ConnectionReturnResult returnResult)
à System.Net.Connection.ReadComplete(Int32 bytesRead, WebExceptionStatus errorStatus)
à System.Net.LazyAsyncResult.Complete(IntPtr userToken)
à System.Net.ContextAwareResult.Complete(IntPtr userToken)
à System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
à System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
à System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)

编辑:上下文在 MessageInspector 之后返回

一旦我们回到主代码分支,上下文就回来了,堆栈也回来了

à MyNamespace.MyClass.<MyMethodAsync>d__5`2.MoveNext()
à System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
à System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
à System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run()
à System.Web.Util.SynchronizationHelper.SafeWrapCallback(Action action)
à System.Threading.Tasks.Task.Execute()
à System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
à System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
à System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)
à System.Threading.Tasks.Task.ExecuteEntry(Boolean bPreventDoubleExecution)
à System.Threading.ThreadPoolWorkQueue.Dispatch()

ASP.NET 处理调用 MessageInspector 的方式似乎有问题。

asp.net wcf async-await

评论

1赞 Stephen Cleary 10/8/2023
We have a .NET FW4.2+ project您必须将 4.5 与 4.5+ 运行时一起使用,并设置为 4.5 或更高版本。更多信息httpRuntime.targetFramework
0赞 Jiayao 10/9/2023
更改为 .Net 4.5 并关闭 Quirks 模式。然后运行代码,如果再次失败,则发布一些日志。还要避免在 ASP.NET 中使用。Task.Run
0赞 bkqc 10/10/2023
我们没有在 4.5+ 中,在 web.config 中将 TargetFramework 设置为 4.5(事实上,Web 项目本身和 dummyInspector 在 4.8 中,尽管我们无法控制的 MessageInspector 在 4.5 中)。看来我看错了。听说过阅读障碍吗?:P请注意,问题仅在检查器中。一旦我们回到主代码,上下文就回来了。
0赞 bkqc 10/10/2023
@Jiayao:我想我现在明白你为什么在谈论它了,但它不是 htat,它是,它是由框架调用的,而不是由我调用的。Task.RunSystem.Threading.ExecutionContext.Run
0赞 Jiayao 10/11/2023
上下文返回后是否还有其他错误?

答: 暂无答案