我目前正在构建一个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 :(得分:13)
有两种通用方法可以达到你想要的效果:
ConfigureAwait(false)
库中使用async
。在您的情况下,您应该在GetLatestPlaces()
方法。在不同的线程中调用async
方法,以便不阻止当前上下文。所以你的代码看起来像这样:
IList<PlaceListItemViewModel> places = Task.Run(async () => await GetLatestPlaces(3)).Result;
有关详细信息,请参阅Stephen Cleary's excellent blog post。
答案 1 :(得分:0)
同步上下文捕获死锁原因。
你可以避免这种情况
var res = await GetLatestPlaces(3).ConfigureAwait(false);
答案 2 :(得分: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()
。