在Razor视图中使用await

是否可以await Razor .cshtml视图中的任务?

默认情况下它抱怨它只能用于标记为async方法,所以我想知道是否有一个隐藏的开关可以启用它?

不,那是不可能的,无论如何你都不应该这样做。 Razor视图应包含标记,最多包含一些帮助程序调用。 async / await属于您的后端逻辑。

我很长一段时间都想要这样的东西 – 如果他们不需要写一堆查询,我们写的很多页面都可以由Jr Dev抛出; 并且,每次都是相同的基本查询样板 – 当他们的大部分工作是为了获得内容时,为什么他们必须为每个Controller编写它们? 我使用C#所以我不必处理内存管理,为什么HTML编码器必须处理查询细节?

您可以使用一种技巧来隐式地将数据异步加载到View中。 首先,定义一个表达所需数据的类。 然后,在每个View的顶部,实例化该类。 回到Controller中,您可以查找您知道将要使用的视图,打开它,然后编译该类 。 然后,您可以使用它以与MVC强制执行的方式在Controller中获取View将需要的数据async。 最后,使用ViewModel将其传递给View作为MVC规定,并且通过一些技巧 – 你有一个View来声明它将使用哪些数据。

这是一个StoryController。 Jr Devs将故事写成简单的.cshtml文件,而不必知道Controller,数据库或LINQ是什么:

 public class StoryController : BaseController { [OutputCache(Duration=CacheDuration.Days1)] // /story/(id) public async Task Id(string id = null) { string storyFilename = id; // Get the View - story file if (storyFilename == null || storyFilename.Contains('.')) return Redirect("/"); // Disallow ../ for example string path = App.O.AppRoot + App.HomeViews + @"story\" + storyFilename + ".cshtml"; if (!System.IO.File.Exists(path)) return Redirect("/"); return View(storyFilename); 

所有这一切现在都是基于URL获取View文件,允许像WebForms这样的东西(除了MVC内部和使用Razor)。 但我们希望在一些标准的ViewModel和Partials中显示一些数据 – 在我们的例子中,数据库中积累的人员和项目。 让我们定义如何并编译出来。 (请注意,在我的情况下,ConservX恰好是核心项目命名空间。)

  public async Task Id(string id = null) { string storyFilename = id; // 1) Get the View - story file if (storyFilename == null || storyFilename.Contains('.')) return Redirect("/"); // Disallow ../ for example string path = App.O.AppRoot + App.HomeViews + @"story\" + storyFilename + ".cshtml"; if (!System.IO.File.Exists(path)) return Redirect("/"); // 2) It exists - begin parsing it for StoryDataIds var lines = await FileHelper.ReadLinesUntilAsync(path, line => line.Contains("@section")); // 3) Is there a line that says "new StoryDataIds"? int i = 0; int l = lines.Count; for (; i < l && !lines[i].Contains("var dataIds = new StoryDataIds"); i++) {} if (i == l) // No StoryDataIds defined, just pass an empty StoryViewModel return View(storyFilename, new StoryViewModel()); // https://stackoverflow.com/questions/1361965/compile-simple-string // https://msdn.microsoft.com/en-us/library/system.codedom.codecompileunit.aspx // https://msdn.microsoft.com/en-us/library/system.codedom.compiler.codedomprovider(v=vs.110).aspx string className = "__StoryData_" + storyFilename; string code = String.Join(" ", (new[] { "using ConservX.Areas.Home.ViewModels.Storying;", "public class " + className + " { public static StoryDataIds Get() {" }).Concat( lines.Skip(i).TakeWhile(line => !line.Contains("};")) ).Concat( new[] { "}; return dataIds; } }" } )); var refs = AppDomain.CurrentDomain.GetAssemblies(); var refFiles = refs.Where(a => !a.IsDynamic).Select(a => a.Location).ToArray(); var cSharp = (new Microsoft.CSharp.CSharpCodeProvider()).CreateCompiler(); var compileParams = new System.CodeDom.Compiler.CompilerParameters(refFiles); compileParams.GenerateInMemory = true; compileParams.GenerateExecutable = false; var compilerResult = cSharp.CompileAssemblyFromSource(compileParams, code); var asm = compilerResult.CompiledAssembly; var tempType = asm.GetType(className); var ids = (StoryDataIds)tempType.GetMethod("Get").Invoke(null, null); using (var db... // Fetch the relevant data here var vm = new StoryViewModel(); return View(storyFilename, vm); } 

这是大部分工作。 现在Jr Devs可以像这样声明他们需要的数据:

 @using ConservX.Areas.Home.ViewModels.Storying @model StoryViewModel @{ var dataIds = new StoryDataIds { ProjectIds = new[] { 4 } }; string title = "Story Title"; ViewBag.Title = title; Layout = "~/Areas/Home/Views/Shared/_Main.cshtml"; } @section css { ... 

这很容易。 这是视图代码:

 @{ DoAsyncStuffWrapper(); } @functions { async void DoAsyncStuffWrapper() { await DoAsyncStuff(); } } 

我知道这是一个较旧的线程,但我会添加我的输入,以防其他人发现它有用。 我在ASP.Net MVC中使用新的MongoDB驱动程序遇到了这个问题 – 新的驱动程序(现在),只实现异步方法并返回异步游标,因为asynccursor没有实现IEnumerable,所以它们不能在foreach中使用。 示例代码通常如下所示:

 while(await cursor.movenextasync) var batch=cursor.current foreach(var item in batch) --do stuff here-- 

但是,这在剃刀中不起作用,因为视图本质上不是异步,等待不会削减它。

我通过将第一行更改为:

 while(cursor.MoveNextAsync().Result) 

返回true,直到光标到达最后一个条目。

希望有所帮助!

如果你真的需要它,你可以做到这一点,它会很难看,但它会起作用。

在视图中

 @{ var foo = ViewBag.foo; var bar = ViewBag.bar; } 

在控制器中

 public async Task Index() { ViewBag.foo = await _some.getFoo(); ViewBag.bar = await _some.getBar(); return View("Index"); }