Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use of await in Razor views

Is it possible to await on tasks in Razor .cshtml views?

By default it complains that it can only be used in methods marked with async so I'm wondering if maybe there is a hidden switch somewhere that enables it?

like image 339
Knaģis Avatar asked Oct 04 '13 13:10

Knaģis


People also ask

How do you call async method in razor view?

There is a trick you can use to sort of implicitly load data async into the View. First, you define a class that expresses what data you want. Then, at the top of each View, instantiate that class. Back in the Controller, you can lookup the View you know you're going to use, open it, then compile that class.

Why we use async and await in MVC?

Async, Await And Asynchronous Programming In MVC. Async keyword is used to call the function/method as asynchronously. Await keyword is used when we need to get result of any function/method without blocking that function/method.

What is the use of await in asp net core?

The await operator suspends evaluation of the enclosing async method until the asynchronous operation represented by its operand completes. When the asynchronous operation completes, the await operator returns the result of the operation, if any.

Why we use async and await in asp net core?

When we don't want to return a result from our async method, we should always return a Task. To validate our asynchronous operations, we have to use the await keyword while calling that operation. When we convert our synchronous code to asynchronous, we have to use the async and await keywords all the way up the chain.


2 Answers

In ASP.NET Core 2.1, you can use await in Razor views.

See https://docs.microsoft.com/en-us/aspnet/core/mvc/views/partial?view=aspnetcore-2.1

Example:

@await Html.PartialAsync("../Account/_LoginPartial.cshtml") 
like image 79
Kirill Rakhman Avatar answered Oct 10 '22 06:10

Kirill Rakhman


I've wanted something like this for a long time - a lot of the pages we write could be thrown together by a Jr Dev if they didn't have to write a bunch of queries; and, it's the same basic query boilerplate every time anyway - why should they have to write them for each Controller, when the majority of their work is to get content up? I use C# so I don't have to deal with memory management, why should an HTML coder have to deal with query details?

There is a trick you can use to sort of implicitly load data async into the View. First, you define a class that expresses what data you want. Then, at the top of each View, instantiate that class. Back in the Controller, you can lookup the View you know you're going to use, open it, then compile that class. You can then use it to go get the data the View will need, async, in the Controller the way MVC enforces. Finally, pass it off with a ViewModel to the View as MVC prescribes, and, through some trickery - you have a View that declares what data it's going to use.

Here's a StoryController. Jr Devs write stories as simple .cshtml files without having to know what a Controller, database or LINQ is:

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

All this does for now is go get the View file based on the URL, allowing something like WebForms (except inside MVC and using Razor). But we want to show some data - in our case, people and projects that accumulate in the database - with some standard ViewModels and Partials. Let's define how and compile that out. (Note that ConservX happens to be the core Project namespace in my case.)

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

That's the majority of the work. Now Jr Devs can just declare the data they need like so:

@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 { ... 
like image 45
Chris Moschini Avatar answered Oct 10 '22 08:10

Chris Moschini