Подсказка/Свободно для имен разделов бритвы?

Итак, у меня есть случай, когда макет эволюционировал, чтобы усложниться. Там обычные вещи, такие как @section styleIncludes{ ... }, затем другие разделы, которые определяют все виды вещей, которые каждая страница может при желании (но почти всегда), указывают на структуру текущей палитры страниц. Причина, по которой все эти разделы являются секциями, заключается в том, что они встроены в структуру макета.

Я нахожу, что делаю копии предыдущих страниц, потому что там 8 или около того разных разделов, вместо того, чтобы пытаться запомнить точное их правописание или копировать/вставлять кусочек еды.

Я думаю, что было бы лучше создать для них свободный API, так что у меня есть объект с 8 функциями, каждый из которых возвращает сам объект, поэтому вы можете сделать что-то вроде Sections.Style(некоторый шаблон текста MVC или бритва?). Breadcrumb (и т.д.)

Основная цель состоит в том, чтобы иметь возможность кодировать эти разделы в соответствии с руководством и сильно набирать имена вместо того, чтобы полагаться на отличную типизацию или копировать/вставлять.

Однако расширения/помощники в бритве возвращают MvcHtmlString, и я полагаю, что @section представлено чем-то совершенно другим.

Не просить вас написать полное решение для меня, а просто некоторые идеи о том, как это сделать.

Какой объект должен вернуть помощник для представления объявления @section? I.e. аналогия MvcHtmlString.

Что бы вы предложили тип параметра для быстрых методов, например Style или Breadcrumb? Я бы хотел, чтобы бритва прошла аналогичную возможность писать бритву в фигурные скобки объявления раздела. Например, возможность доступа к локальным переменным, объявленным на странице бритвы, так же, как вы можете сделать с объявлением регулярного раздела. Мне не нужно что-то вроде конкатенации строк, например .SomeSection("<div...>Bunch of html stuffed in a string</div>")

Другими словами, если многие из моих страниц cshtml начинают что-то вроде

@{
  string title = "Edit Person"
  ViewBag.Title = title;
}
@section styles{
  .someOneOffPageSpecificStyle { width:59px }
}
@section javascript{
  //javascript includes which the layout will place at the bottom...
}
@section breadcrumb{
  <a ...>Parent Page</a> &gt; <a ...>Sub Page</a> &gt; @title
}

Я бы предпочел бы, чтобы seom вроде беглого API, как это, не совсем для результирующего стиля кода, а скорее потому, что будет легче писать код и не иметь проблем с опечатками и т.д., так как intellisense будет помогать:

@{
  string title = "Edit Person"
  ViewBag.Title = title;
}
@Sections
.Styles(@<text>
  .someOneOffPageSpecificStyle { width:59px }
</text>)
.Javascript(@<text>
  //javascript includes which the layout will place at the bottom...
</text>)
.Breadcrumb(@<text>
  <a ...>Parent Page</a> &gt; <a ...>Sub Page</a> &gt; @title
</text>)

Ответ 1

Скорее всего, это невозможно (используя разделы).

Сначала мы должны понять, как работает MVC под капотом. Мы пишем файлы cshtml, но эти файлы в конечном итоге будут скомпилированы в .NET class, который создается, а затем выполняются методы (на минимальном уровне Execute()), который (большую часть времени) записывает в буфер ответа для IIS для возврата (очень похоже, если не совсем то же самое, что и RenderAction works - вызывает метод дочернего действия и выводит результат в виде родительского представления). HtmlHelpers (или любые пользовательские помощники) просто вызывается из класса instaniated (как делегаты), поэтому они могут выполняться только после компиляции кода в файлах cshtml.

System.Web.Razor.Generator.SectionCodeGenerator требует определения строки для создания разделов. Поэтому, когда вы определяете раздел, строка должна существовать в файле cshtml до того, как файл будет скомпилирован, поскольку HtmlHelper или/и пользовательские помощники не будут выполнены до тех пор, пока файл не будет скомпилирован, не существует способа написать класс или объект, который может обновить файл cshtml до его компиляции.

Что вы можете сделать, это написать собственный HtmlHelper или другие пользовательские помощники, чтобы сделать что-то похожее на то, что предоставляют разделы (без фактического использования каких-либо разделов). Например Я написал это, потому что мне нужно было написать Javascript из частичных представлений и/или шаблонов (которые вы не можете делать с разделами). Если вам необходимо использовать разделы, это может не помочь.

Следующий код пример только не работает как бритва (вообще, после просмотра какого-то кода). Но для того, чтобы этот пример имел смысл, я буду использовать бритва-иш как код и соглашения об именах.

layout.cshtml

<html>
<body>
@RenderSection("MySectionName")

@RenderBody();
</body>
</html>

Index.cshtml

@{
  _layout = "layout";
}

@section MySection {
  <div>MySection</div>
}

<div>My Body</div>

Может быть, скомпилирован в класс, который напоминает:

public class app_aspnet_layout : System.Web.Mvc.WebViewPage
{

  public Execute()
  {
    throw new NotImplementedException();
  }

  public void ExecutePageHierarchy(WebPageContext pageContext, 
                                   TextWriter writer)
  {
    writer.Write("<html>")
    writer.Write("<body>")

    var section = pageContext.SectionWriters["MySectionName"];
    section();

    pageContext.View.ExecutePageHierarchy(null, writer)

    writer.Write("</body>")
    writer.Write("</html>")
  }
}

public class app_aspnet_index : System.Web.Mvc.WebViewPage
{ 
  // generated from the _layout Definition
  private WebViewPage startPage = new app_aspnet_layout();

  public Execute()
  {
     WebPageContext pageContext = new WebPageContext();
     pageContext.View = this;

     pageContext.SectionWriters.Add("MySectionName", 
                                    this.Section_MySectionName);

     var writer = HttpContext.Current.Response.Stream.AsTextWriter();

     if (startPage != null)
     {
       startPage.ExecutePageHierarchy(pageContext, writer);
     }
     else
     {
       this.ExecutePageHierarchy(pageContext, writer);
     }
  }

  // html generated from non-section html
  public void ExecutePageHierarchy(WebPageContext pageContext, 
                                   TextWriter writer)
  {
    writer.Write("<div>My Body</div>");
  }

  public void Section_MySectionName(TextWriter writer)
  {
    writer.Write("<div>MySection</div>");
  }
}

Ответ 2

Если у вас resharper, я бы предложил шаблон живого кода (мощный фрагмент).

Вы можете создать шаблон с помощью одного параметра. Источником макроса этого параметра может быть список значений, разделенных запятыми. Когда вы используете шаблон/фрагмент, он представит вам поле intellisense с именами разделов, доступными для выбора.

Предполагая, что имена разделов, которые вы используете, не будут сильно меняться, вам не придется часто редактировать шаблон, чтобы включать новые имена разделов.

Ответ 3

Вы можете решить проблему в другом направлении - используя t4-шаблоны.

Если структура вашего макета теперь достаточно стабильна (т.е. часто не добавляются и удаляются разделы), и все, что вам нужно, это перестать копировать и вставлять материал при создании новых представлений - это, вероятно, будет работать очень хорошо. Он создаст представления для вас по вашим спецификациям - вы можете сделать некоторые довольно умные вещи в них с отражением и другой логикой

Существует nuget для загрузки шаблонов кода - поиск "codetemplates" - и они довольно прямолинейны для использования.