3

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

在我引入 async/await 之前一切正常,我遇到了这个问题。@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 吗?AspNetSyncCtx 如何在 ASP.NET 中设置?

-- 可能的解决方案。

在@Stephen-cleary 之后我继续定义了一个自定义 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; }
    }
}
4

1 回答 1

2

我遇到了您提到的问题(错误?),即使在启用和必需的情况下,托管在 IIS 中的 WCF 服务中的代码HttpContext.Current也没有流动。asyncaspNetCompatiblity

我的项目使用LogicalCallContextwithCallContext.LogicalSetDataCallContext.LogicalGetDataStephen Cleary 在这篇博文中描述的那样工作。我认为因此设置/获取您的业务上下文在您的情况下会很好地工作,并且可能比 custom 更轻且在概念上更简单SynchronizationContext。在您的情况下,无论如何您都必须在某个地方设置您的业务上下文......不妨将其设置为LogicalCallContext,我相信一切都会好起来的。

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

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

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

我通过创建一个小助手类解决了(kludged?)我在 .NET 4.5 中的问题ContextHelper,并将所有调用替换HttpContext.CurrentContextHelper.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 调用的入口点必须PrepareHttpContextFlow在第一次调用之前调用该方法,await这无疑是有问题的,也是我认为这是一个混乱的主要原因。

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

于 2013-11-06T20:53:57.207 回答