2013-08-29 102 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時丟失的錯誤日誌記錄上下文(因爲異常中的物理堆棧信息不是很好在這種情況下對錯誤日誌很有用)。所以至少如果我記得做一個「準備異步」的電話,我應該記得做另一個:)