Возможно ли выполнить await
в задачах в Razor.cshtml views?
По умолчанию он жалуется, что его можно использовать только в методах, отмеченных async
, поэтому мне интересно, может быть, есть где-то скрытый коммутатор, который позволяет это?
Возможно ли выполнить await
в задачах в Razor.cshtml views?
По умолчанию он жалуется, что его можно использовать только в методах, отмеченных async
, поэтому мне интересно, может быть, есть где-то скрытый коммутатор, который позволяет это?
Нет, это невозможно, и вам все равно не нужно это делать. Представления Razor должны содержать разметку и не более одного вспомогательного вызова. async/await принадлежит вашей логике бэкэнд.
В ASP.NET Core 2.1 вы можете использовать await
в представлениях Razor.
См. Https://docs.microsoft.com/en-us/aspnet/core/mvc/views/partial?view=aspnetcore-2.1.
Пример:
@await Html.PartialAsync("../Account/_LoginPartial.cshtml")
Я хотел что-то подобное в течение долгого времени - многие страницы, которые мы пишем, могут быть выброшены вместе Jr Dev, если им не нужно писать кучу запросов; и в любом случае это один и тот же базовый шаблон запроса - зачем им приходится писать их для каждого Контролера, когда большая часть их работы заключается в том, чтобы получить контент? Я использую С#, поэтому мне не нужно иметь дело с управлением памятью, почему кодер HTML должен иметь дело с деталями запроса?
Есть трюк, который вы можете использовать для сортировки неявно загружать данные async в представление. Во-первых, вы определяете класс, который выражает, какие данные вы хотите. Затем, в верхней части каждого представления, создайте экземпляр этого класса. Вернемся в Контроллер, вы можете найти View, который, как вы знаете, собираетесь использовать, открыть его, а затем скомпилировать этот класс. Затем вы можете использовать его, чтобы получить данные, которые вам понадобятся, async, в контроллере, как работает MVC. Наконец, передайте его с помощью ViewModel в представление как MVC, и через некоторые обманы - у вас есть представление, которое объявляет, какие данные он будет использовать.
Здесь находится StoryController. Jr Devs пишет истории как простые файлы .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);
Все это на данный момент - это получить файл View на основе URL-адреса, позволяя что-то вроде WebForms (кроме MVC и используя Razor). Но мы хотим показать некоторые данные - в нашем случае - люди и проекты, которые накапливаются в базе данных - с некоторыми стандартными 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 {
...
Я попал на этот вопрос, потому что я новичок в Razor и хотел показать простой экран "загрузка...", пока мой код контроллера вычислял данные.
Так что я нашел эту ссылку: https://www.codeproject.com/Articles/424745/MVC-Razor-In-Progress-Icon, которая была полезна, но, поскольку я был новичком в Razor, я не смог сделать эту работу,
То, что в итоге сработало для меня, было следующим.
1) Добавьте div "loading", как предложено в проекте кода, в мой файл .cshtml:
<div id="divLoading" style="margin: 0px; padding: 0px; position: fixed; right: 0px;
top: 0px; width: 100%; height: 100%; background-color: #666666; z-index: 30001;
opacity: .8; filter: alpha(opacity=70);display:none">
<p style="position: absolute; top: 30%; left: 45%; color: White;">
Loading, please wait...<img src="../../Content/Images/ajax-loading.gif">
</p>
</div>
2) Изменить мою форму бритвы из
<input type="submit" value="Go"/>
в
<input type="button" value="Go" onclick="JavascriptFunction()" />
3) Создайте JavascriptFunction() на моей странице .cshtml:
<script type="text/javascript" language="javascript">
function JavascriptFunction() {
$("#divLoading").show();
$('form').submit();
}
</script>
Если я правильно понимаю все вышеперечисленное, то при нажатии кнопки "Перейти" выполняется функция JavascriptFunction.
Функция JavascriptFunction делает 2 вещи: 1) Изменить вид страницы, показывая ранее скрытый (display: none) divLoading div. 2) Отправьте все формы на этой странице (у меня есть только одна, поэтому она отправляет форму так же, как если бы у меня был тип отправить на кнопку)
После того, как Контроллер, запущенный отправкой формы, завершает работу, он загружает новый вид на новую страницу, и исходная страница (и div "loading") исчезает. Миссия выполнена.
Я знаю, что это более старый поток, но я добавлю свой вклад на случай, если кто-то найдет его полезным. Я столкнулся с этой проблемой, работая с новым драйвером MongoDB в ASP.Net MVC - новым драйвером (на данный момент), только реализует методы async и возвращает асинхронные курсоры, которые нельзя использовать в foreach, потому что asynccursor не реализует IEnumerable, Пример кода обычно выглядит так:
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<ActionResult> Index()
{
ViewBag.foo = await _some.getFoo();
ViewBag.bar = await _some.getBar();
return View("Index");
}
Это на самом деле легко. Вот код вида:
@{
DoAsyncStuffWrapper();
}
@functions {
async void DoAsyncStuffWrapper()
{
await DoAsyncStuff();
}
}