Razor не анализирует в том порядке, в котором я ожидал

примечание: <!-- [..] --> представляет исключенный код для ясности


С _Layout следующим образом

<!-- [..] -->
    @Html.EmitRequiredStylesheet()
</head>
<body>
    @await Html.PartialAsync("_Header");
    <div class="container body-content">
        @RenderBody()
    </div>
    @await Html.PartialAsync("_Footer");
<!-- [..] -->

партиал _Footer, содержащий следующий код

<!-- [..] -->
@Html.RequireStylesheet("/css/Footer.css")
<!-- [..] -->

И RequireStylesheet()а также EmitRequiredStylesheet() это два метода расширения, которые я адаптировал к .Net Core 2.0 из этот пост следующим образом.

    public static string RequireStylesheet(this IHtmlHelper html, string path, int priority = 1)
    {
        var contextItems = html.ViewContext.HttpContext.Items;
        var requiredScripts = contextItems["RequiredStylesheets"] as List<ResourceInclude> ?? new List<ResourceInclude>();

        if(requiredScripts.All(i => i.Path != path))
            requiredScripts.Add(new ResourceInclude() {Path = path, Priority = priority});

        return null;
    }

    public static HtmlString EmitRequiredStylesheet(this IHtmlHelper html)
    {
        var contextItems = html.ViewContext.HttpContext.Items;

        if(!(contextItems["RequiredStylesheets"] is List<ResourceInclude> requiredScripts))
            return null;

        var sb = new StringBuilder();
        foreach(var item in requiredScripts.OrderByDescending(i => i.Priority))
        {
            sb.Append($"<link ref=\"{item.Path}\" type=\"stylesheet\" />\n");
        }

        return new HtmlString(sb.ToString());
    }

Что я пытаюсь здесь сделать, так это иметь частичное представление, способное сообщить _Layout, какие таблицы стилей/скрипты ему нужны. Как объясняет этот ответ, порядок синтаксического анализа Razor означает, что макет будет анализироваться последним. Но из ответа я ожидал, что мое частичное представление _Footer будет проанализировано и выполнено до самого _Layout, а это не так. Отладка с использованием точек останова показала, что вызов @Html.EmitRequiredStylesheet() выполняется до вызова @Html.RequireStylesheet("/css/Footer.css"), что означает, что таблица стилей еще не зарегистрирована, когда вызывается первый.

Этот код отлично работал бы, если бы партиал _Footer вызывался из кода, отображаемого @RenderBody(), но это не так (это часть макета).

Есть ли какой-нибудь обходной путь, например способ сообщить Razor, что данный партиал должен быть выполнен до самого макета?

Примечание: даже если было бы целесообразно включить таблицу стилей непосредственно в _Layout, я бы хотел, чтобы она была как можно ближе к коду, с которым она связана. Я действительно не большой поклонник массивных централизованных конфигурационных файлов, и я предпочитаю, чтобы мой include/config выполнялся как можно ближе к коду.


person Mathieu VIALES    schedule 01.11.2017    source источник
comment
Я могу ошибаться, но я полагаю, что razor генерирует вашу страницу в следующем порядке: 1. RenderBody() выполняется сверху вниз, частичные просмотры в порядке их появления 2. _Layout выполняется сверху вниз, вставляя результаты RenderBody() туда, где он должен. E: - Чтобы сделать то, что вы хотите, вам, вероятно, придется создать некоторые пользовательские функции отложенного рендеринга, а в конце страницы макета сделать ExecuteDelayedRenders()   -  person Neil    schedule 01.11.2017
comment
Значит, у меня нет встроенных способов сделать это? Хм, хорошо, спасибо. Попробую сделать так, как вы посоветовали. Спасибо.   -  person Mathieu VIALES    schedule 01.11.2017