2016-12-26 44 views
0

在我的ASP.NET MVC應用程序中,我需要實現/重寫一些內部實體,如IModelBinder.BindModel(),IActionFilter.OnActionExecuting()等。這些方法調用異步方法,但顯然我無法將它們變成async Task<>以便能夠使用關鍵字await如何在同步調用的任務中使HttpContext可用?

所以我寫了下面的「適配器」:

public static T GetResult<T>(Func<Task<T>> func) 
{ 
    var httpContext = HttpContext.Current; 

    var proxyTask = Task.Run(() => 
    { 
     HttpContext.Current = httpContext; 
     return func(); 
    }); 

    return proxyTask.Result; 
} 

這讓我打電話給我的異步GetData()同步方式:

public static async Task<Data> GetData() 
{ 
    if (isCached) 
     return GetCachedData(); 

    var data = await GetOriginalData(); 
    SetCachedData(data); 
    return data; 
} 

... 

var data = GetResult(() => GetData()); 

好了,它的作品,所以我會叫它但是正如你所看到的,大部分時候,GetData()同步運行,因此無條件產生一個新線程並不是很好的性能。這是竊聽我,所以我已經結束了一個不同的解決方案:

public static T GetResult<T>(Func<Task<T>> func) 
{ 
    var syncContext = SynchronizationContext.Current; 
    SynchronizationContext.SetSynchronizationContext(null); 

    var task = func(); 

    SynchronizationContext.SetSynchronizationContext(syncContext); 

    return task.Result; 
} 

它也可以,但問題是,既然SynchronizationContext不再流入,存在被調用的方法沒有HttpContext可用。

我可以通過將HttpContext.Current合併到邏輯CallContext中,但我仍然想知道是否有更好的方法來解決這個問題?像定製SynchronizationContext.Current.CreateCopy()

+1

首先,閱讀[*我應該暴露同步包裝爲異步方法?*] [1]和[*我應該暴露異步包裝器同步的方法?*] [2] Stephan的Toub。 –

+1

爲什麼你認爲你需要以同步的方式調用你的異步API? –

+1

@PauloMorgado,鏈接被破壞。就像我說的,我需要實現'object IModelBinder.BindModel()','void IActionFilter.OnActionExecuting()','bool IRouteConstraint.Match()'等等,這不會給我任何選擇。 –

回答

3

產生一個新的線程無條件

其實,從線程池只是借用一個線程簡單。幾乎沒有開始一個新的線程那樣糟糕,但仍然不是最好的性能。

它也有效,但問題是由於SynchronizationContext不再流入,所以在被調用的方法中沒有可用的HttpContext。

確定嗎?我期望HttpContext.Current設置在GetData的開頭方式來調用它。我也希望HttpContext.Currentnull之後的await均爲的方式來調用它。如果它在而不是null之後await在您的測試中,這可能是因爲延續剛剛發生在同一個線程上。

這是嘗試在請求上下文之外使用HttpContext.CurrentSynchronizationContext)的主要問題之一。當你將它放在一個裸線程上(Task.Run)時,它將一直呆在那裏,直到該線程進入另一個請求上下文。

我大概可以工作,圍繞通過把HttpContext.Current爲邏輯CallContext中

我會建議在拔出你Current而需要對原請求上下文的任何數據,然後通過數據明確地指向需要它的方法。

至於原來的問題(避免在同步情況下,額外的線程),我建議你只是把同步TryGetData方法。