2013-10-04 57 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 { 
...