【问题标题】:Using the AspNetSynchronizationContext from WCF hosted in IIS使用 IIS 中托管的 WCF 中的 AspNetSynchronizationContext
【发布时间】:2013-08-29 20:21:22
【问题描述】:

我在 IIS 中托管我的 .NET 4.5 WCF 服务。 OperationContext.Current 实例中存储了一条名为“BusinessContext”(BC) 的信息,因此下游的任何逻辑都可以访问它。

在我引入 async/await 之前一切正常,我遇到了 this issue。 @stephen-cleary 提到 ASP.NET 使用异步友好的 AspNetSynchronizationContext 来保持 HttpContext.Current 跨线程。由于我在 IIS 中托管,我想我应该能够利用 WCF 中的 AspNetSyncCtx,并使用 HttpContext.Current 而不是 OperationContext 来存储 BC。

我从头开始创建了一个 WCF 服务,它在 Web.config 中默认设置了 targetFramework = 4.5、aspnet:UseTaskFriendlySynchronizationContext = true 和 aspNetCompatibilityEnabled = true。我还在我的服务中添加了 AspNetCompatibilityRequirements = Required。

在运行时我看到 HttpContext.Current 存在,但 SynchronizationContext.Current 为空。在等待之后,HttpContext 变为空,这是预期的,因为没有 SyncCtx。当需要 aspcompatibility 时,不应该将其设置为 AspNetSyncCtx 吗?在 ASP.NET 中如何设置 AspNetSyncCtx?

-- 可能的解决方案。

按照@Stephen-cleary 的here,我继续定义了一个自定义 SynchronizationContext 以跨线程保留 OperationContext。

我想听听社区对此实施的意见。谢谢。

public class OperationContextSynchronizationContext : SynchronizationContext
{
    public override void Post(SendOrPostCallback d, object state)
    {
        OperationContext opCtx = OperationContext.Current;
        InternalState internalState = new InternalState()
        {
            OpCtx = opCtx,
            Callback = d,
            State = state,
            SyncCtx = this
        };
        ThreadPool.QueueUserWorkItem(new WaitCallback(InternalInvoker), internalState);
    }
    private void InternalInvoker(object internalState)
    {
        InternalState internalSt = internalState as InternalState;
        SynchronizationContext.SetSynchronizationContext(internalSt.SyncCtx);
        using (new OperationContextScope(internalSt.OpCtx))
        {
            internalSt.Callback.Invoke(internalSt.State);
        }
    }
    private class InternalState
    {
        public SynchronizationContext SyncCtx { get; set; }
        public OperationContext OpCtx { get; set; }
        public SendOrPostCallback Callback { get; set; }
        public object State { get; set; }
    }
}

【问题讨论】:

    标签: wcf asynchronous async-await


    【解决方案1】:

    我遇到了您提到的问题(错误?),即使启用并需要 aspNetCompatiblity,在 IIS 中托管的 WCF 服务中 HttpContext.Current 代码也没有与 async 一起流动。

    我的项目使用LogicalCallContextCallContext.LogicalSetDataCallContext.LogicalGetData 进行工作,正如Stephen Cleary 在this blog post 中所描述的那样。我认为因此设置/获取您的业务上下文在您的情况下会非常有效,并且可能比自定义 SynchronizationContext 更轻巧且概念上更简单。在您的情况下,无论如何您都必须在某个地方设置您的业务上下文......不妨将其设置为LogicalCallContext,我相信一切都会好起来的。

    我将更详细地解释我个人的“解决方案”,尽管我承认这有点笨拙,因为我手动流动整个 HttpContext 对象(并且仅在 WCF 方法中)。但是,您使用自定义上下文对象的情况似乎更合适。

    在我继承的 ASP.NET Web 应用程序中,对 HttpContext.Current 的调用遍布整个业务逻辑(不理想)。显然,该项目运行良好,直到我希望应用程序中的几个 WCF 方法为 async

    业务逻辑中的许多调用来自 ASP.NET 页面加载等,一切正常运行。

    我通过创建一个小助手类 ContextHelper 解决了我在 .NET 4.5 中的问题,并将所有对 HttpContext.Current 的调用替换为 ContextHelper.CurrentHttpContext

    public class ContextHelper
    {
        public static void PrepareHttpContextFlow()
        {
            CallContext.LogicalSetData("CurrentHttpContext", HttpContext.Current);
        }
    
        public static HttpContext CurrentHttpContext
        {
            get
            {
                return HttpContext.Current ?? 
                       CallContext.LogicalGetData("CurrentHttpContext") 
                       as HttpContext;
            }
        }
    }
    

    现在只要定义了HttpContext.Current,我的辅助方法就基本上是空操作。

    为了完成这项工作,我的 WCF 调用的入口点必须在第一次调用 await 之前调用 PrepareHttpContextFlow 方法,这无疑是有问题的,也是我认为这是杂乱无章的主要原因。

    在我的情况下,这个缺点通过以下事实得到缓解:我需要一些其他手动调用来添加逻辑堆栈信息以添加在使用 async 时丢失的错误日志记录上下文(因为异常中的物理堆栈信息不是在这种情况下对错误日志非常有用)。所以至少如果我记得做一个“准备异步”调用,我应该记得做另一个:)

    【讨论】:

      猜你喜欢
      • 2023-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-09
      • 1970-01-01
      • 2012-08-10
      • 2011-03-03
      相关资源
      最近更新 更多