2013-10-04 73 views
19

在Razor .cshtml视图中的任务上可以使用await吗?在剃刀视图中使用等待

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

+9

好像你会在控制器中做的事情,或者在后台代码中进一步堆栈。 –

回答

16

不,这是不可能的,你不需要这样做。 Razor视图应该包含标记和最多一些帮助程序调用。异步/等待属于您的后端逻辑。

+13

Downvote为“不要这样做”的一部分。在数据到达之前渲染第一个字节并不奇怪,视图规定的数据也不需要。例如,如果我递交了实习生Razor,并且他们需要将页面放在一起而不知道如何调用数据库,那么我可以教他们通过一个非常简单的界面通过懒加载的回购来调用数据 - 除非您遇到此墙的异步(可能)在Razor中受到限制。 –

+2

@ChrisMoschini根本没有任何意义。 “视图需求的数据需求”是ViewModel的内容,它已经由控制器提供。 “在数据到达之前渲染”仅仅是以错误的方式看待缓慢的数据检索问题。这是缓存。 –

+0

@ssg考虑每个人每次访问时都有所不同的页面,并且需要一些时间来计算结果。假设它是一个东西的列表。更好的方式是,无缓冲地将输出流渲染到输出流中,而不是让用户等待,直到所有内容都到达为止。 ViewModel方法会给你带来糟糕的后果。让视图实时获取和渲染数据可以为您提供更好,低时间的第一结果前结果。 –

-1

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

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

但是,这并不在剃须刀工作,因为观点本身不支持异步,伺机不剪。

我得到它通过改变第一线的工作:直到光标命中的最后一个条目

while(cursor.MoveNextAsync().Result) 

返回true。

希望有帮助!

+1

正确答案是“你做错了”。这应该在控制器的方法中以异步方式完成。试图从视图直接谈论数据库,绕过控制器,模型和任何数据层都与MVC完全相反,并且简单地导致渲染延迟。该视图应该只是在视图模型中呈现数据。视图模型应该只包含该视图所需的数据。它是*控制器*作业来检索数据并构建DTO /模型并将其发送到视图 –

+0

@PanagiotisKanavos这并不意味着对于大型列表从数据库中逐一检索渲染并实际发送输出到客户端不会发生,直到完整列表加载到内存?在控制器中使用逻辑确实很有意义,但是对于我提到的情况,能够通过某种异步枚举并逐个异步地在视图中对其进行渲染不是更好吗?或者是有一些技术限制吗? –

+0

@martinh_kentico不,完全没有。如果您逐个加载条目,则数据访问逻辑中存在严重问题。 *为什么*当一个查询将一次返回*全部*结果时一个接一个加载?为什么当你不能*显示结果时返回*很多结果?为什么不使用*分页*在这种情况下?为什么不在LINQ中使用'.Contains()'子句,或者在SQL中使用'IN(..)'来传递一个ID列表? –

-1

如果你真的需要它,你可以做到这一点,这将是丑陋的,但它会工作。

在查看

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

在控制器

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

这相当于var foo = await ... bar = await ... return View(“Index”,new {foo = foo,bar = bar}) - 您仍然只是通过在await/async中加载数据正常的控制器,而不是剃刀。 –

0

,我想这样的事情很长一段时间 - 很多我们写可能是由JR开发,如果被扔在一起的页面他们不必写一堆查询;而且,无论如何它每次都是同样的基本查询样板文件 - 为什么他们必须为每个控制器编写它们,而大多数工作是为了满足内容?我使用C#,所以我不必处理内存管理,为什么HTML编码器不得不处理查询细节?

有一种技巧可以用来将数据异步隐式加载到视图中。首先,你定义一个表达你想要的数据的类。然后,在每个视图的顶部,实例化该类。回到Controller中,你可以查看你知道你要使用的View,打开它,然后编译那个类。然后,您可以使用它以MVC强制执行的方式获取View中需要的数据,即异步。最后,将ViewModel传递给MVC规定的视图,并通过一些欺骗手段 - 你有一个视图来声明它将要使用的数据。

这是一个StoryController。 JR离散事件写故事一样简单.cshtml文件,而无需知道什么是控制器,数据库或LINQ是:

public class StoryController : BaseController 
{ 
    [OutputCache(Duration=CacheDuration.Days1)] 
    // /story/(id) 
    public async Task<ActionResult> 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查看文件,允许类似的WebForms(除了在MVC和使用剃刀之外)。但我们想要展示一些数据 - 在我们的例子中,数据库中积累的人员和项目 - 带有一些标准的ViewModels和Partials。我们来定义如何编译出来。(请注意,ConservX恰好是我的案例中的核心Project命名空间。)

public async Task<ActionResult> 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 { 
...