2013-08-29 31 views
3

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

一切正常,直到我引入异步/等待,我碰到了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 =我的服务必需的。

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

- 可能的解决方案。

正在关注@ 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; } 
    } 
} 

回答

2

我跑进问题(错误?)你提到,其中HttpContext.Current没有在IIS托管甚至aspNetCompatiblity启用,需要WCF服务,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.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调用的入口点必须在第一次调用await之前调用PrepareHttpContextFlow方法,这是公认的问题,也是我认为这是一个混乱的主要原因。

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