Подтвердить что ты не робот

Использование ожиданий в представлении Razor

Возможно ли выполнить await в задачах в Razor.cshtml views?

По умолчанию он жалуется, что его можно использовать только в методах, отмеченных async, поэтому мне интересно, может быть, есть где-то скрытый коммутатор, который позволяет это?

4b9b3361

Ответ 1

Нет, это невозможно, и вам все равно не нужно это делать. Представления Razor должны содержать разметку и не более одного вспомогательного вызова. async/await принадлежит вашей логике бэкэнд.

Ответ 3

Я хотел что-то подобное в течение долгого времени - многие страницы, которые мы пишем, могут быть выброшены вместе 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 {
...

Ответ 4

Я попал на этот вопрос, потому что я новичок в 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") исчезает. Миссия выполнена.

Ответ 5

Я знаю, что это более старый поток, но я добавлю свой вклад на случай, если кто-то найдет его полезным. Я столкнулся с этой проблемой, работая с новым драйвером 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, пока курсор не попадет в последнюю запись.

Надеюсь, что это поможет!

Ответ 6

Если вам это действительно нужно, вы можете сделать это, это будет уродливо, но он будет работать.

В представлении

@{  
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");
        }

Ответ 7

Это на самом деле легко. Вот код вида:

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