2015-05-27 12 views
6

我目前正在構建一個ASP.NET MVC應用程序。我正在嘗試向我的網站中的所有頁面的ViewBag屬性添加項目。爲了達到這個目的,我創建了一個基本控制器,所有我的站點控制器都從中繼承。在被覆蓋的空洞中運行異步方法

爲了增加ViewBag,我已經覆蓋了OnActionExecuting方法:protected override void OnActionExecuting(ActionExecutingContext filterContext)

我知道,MVC5的OnActionExecuting方法不是異步,這就是爲什麼我遇到這個問題。我需要能夠調用某種形式下面的代碼來獲取最新的項目,並把它們放到ViewBag:

IList<PlaceListItemViewModel> places = await GetLatestPlaces(3); 
ViewBag.FooterLatestPlaces = places; 

使用GetLatestPlaces(3).Result只是導致死鎖,所以這不是一個選項。

有沒有人可以給我一個選擇,如何實現這一點。它不必重寫OnActionExecuting方法,如果有另一種方法爲每個頁面添加項目到ViewBag(除了從每個控制器中的每一個動作調用代碼),我都願意使用該方法。

不幸的是,我不能將GetLatestPlaces方法設置爲非異步方式,因爲它使用的是MongoDB 2.0驅動程序,它一路是異步的。

+0

考慮重新設計,以便稍後可以調用該方法...實際上沒有任何方法可以同步調用異步方法。 –

+0

你以後說的方法是什麼?有沒有另一種方法可以將數據添加到ViewBag(或類似的),可以在剃鬚刀視圖文件中使用,而不需要將大量的C#代碼添加到視圖中? – Juzzbott

回答

10

避免這種情況一般有兩種方法來實現你想要的東西:

  1. 使用ConfigureAwait(false)async庫。在你的情況下,你應該在GetLatestPlaces()方法中做到這一點。
  2. 在不同的線程中調用您的async方法以便不阻止當前上下文。所以,你的代碼看起來就像這樣:

    IList<PlaceListItemViewModel> places = Task.Run(async() => await GetLatestPlaces(3)).Result; 
    

欲瞭解更多信息請參閱Stephen Cleary's excellent blog post

+0

你描述的第二種方法正在出色地工作!我一定會讀那篇博文。乾杯。 – Juzzbott

+0

@Juzzbott第一種方法也應該工作,閱讀斯蒂芬的博客文章,你會得到所有的概念。 –

+2

由於不必要的上下文切換,在ASP.NET應用程序中調用Task.Run會損害性能。 –

0

死鎖的原因是捕獲的同步上下文。 您可以通過 var res = await GetLatestPlaces(3).ConfigureAwait(false);

+0

我不能使用await關鍵字,因爲我沒有使用異步方法。我無法將其更改爲異步Task <>方法,因爲我重寫了Controller類中存在的OnActionExecuting(ActionExecutingContext filterContext)方法。 – Juzzbott

+1

你可以使它異步無效,但它真的不是個好主意。 – rnofenko

0

下面是允許你調用以同步方式異步方法,在性能方面的開銷和死鎖的方法:

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; 
} 

這種方式,你會打電話

var places = GetResult(() => GetLatestPlaces(3)); 

但是,請注意,由於當前SynchronizationContext未被捕獲,任何綁定到它的上下文都不會流入該任務。在你的例子中,HttpContext.Current將不會在GetLatestPlaces()中可用。