Uso dell’attesa nelle visualizzazioni del razor

È ansible await attività nelle viste Razor .cshtml?

Di default si lamenta che può essere usato solo nei metodi segnati con async quindi mi chiedo se forse c’è un interruttore nascosto da qualche parte che lo abilita?

No, non è ansible e non dovresti farlo comunque. Le visualizzazioni del razor dovrebbero contenere markup e al massimo qualche chiamata di aiuto. async / await appartiene alla tua logica backend.

Ho desiderato qualcosa di simile per molto tempo – molte delle pagine che scriviamo potrebbero essere messe insieme da un Jr Dev se non dovessero scrivere un sacco di domande; e, comunque, è sempre la stessa domanda di base boilerplate ogni volta – perché dovrebbero doversene scrivere per ogni Controller, quando la maggior parte del loro lavoro è quello di ottenere il contenuto? Uso C # quindi non devo occuparmi della gestione della memoria, perché un codificatore HTML deve occuparsi dei dettagli della query?

C’è un trucco che puoi usare per ordinare implicitamente caricare i dati asincroni nella vista. Innanzitutto, si definisce una class che esprime quali dati si desidera. Quindi, in cima a ciascuna vista, crea un’istanza di quella class. Tornando al Controller, puoi cercare la Vista che sai che stai per usare, aprirla, quindi compilare quella class . È quindi ansible utilizzarlo per ottenere i dati necessari per la visualizzazione, asincrona, nel controller come applica MVC. Infine, passalo con un ViewModel alla vista come prescrive MVC e, attraverso alcuni trucchi, hai una vista che dichiara quali dati verrà utilizzato.

Ecco un StoryController. Jr Devs scrive storie come semplici file .cshtml senza dover sapere cos’è un controller, database o 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); 

Tutto questo per ora è andare a ottenere il file View basato sull’URL, permettendo qualcosa come WebForms (eccetto all’interno di MVC e usando Razor). Ma vogliamo mostrare alcuni dati – nel nostro caso, persone e progetti che si accumulano nel database – con alcuni ViewModels e Partials standard. Definiamo come e compilalo. (Si noti che ConservX sembra essere lo spazio dei nomi di progetto principale nel mio caso).

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

Questa è la maggior parte del lavoro. Ora Jr Devs può semplicemente dichiarare i dati di cui hanno bisogno in questo modo:

 @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 { ... 

In realtà è facile. Ecco il codice della vista:

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

So che questo è un thread più vecchio, ma aggiungerò il mio input nel caso in cui qualcun altro lo trovi utile. Ho incontrato questo problema lavorando con il nuovo driver MongoDB in ASP.Net MVC – il nuovo driver (per ora), implementa solo i metodi asincroni e restituisce i cursori asincroni, che non possono essere utilizzati in un foreach perché asynccursor non implementa IEnumerable . Il codice di esempio appare in genere:

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

Ma questo non funziona nel razor, perché le viste non sono intrinsecamente asincrone e l’attesa non la taglia.

L’ho fatto funzionare cambiando la prima linea in:

 while(cursor.MoveNextAsync().Result) 

che restituisce true finché il cursore non raggiunge l’ultima voce.

Spero possa aiutare!

Se ne hai davvero bisogno, puoi farlo, sarà brutto, ma funzionerà.

In vista

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

Nel controller

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