Работа с Razor в представлении

100

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

Сложность в механизм визуализации привносится системой шаблонов представлений, которая включает фрагменты кода, поддерживает компоновки и скомпилирована для оптимизации производительности. Мы не делали ничего подобного в своем простом механизме визуализации, и по большому счету не должны это делать, потому что обо всем позаботится механизм Razor.

В Razor доступна функциональность, которую требуют практически все приложения MVC. Необходимость в создании специального механизма визуализации возникает лишь в очень малом числе проектов. Основы синтаксиса Razor были даны статьях Модели и компоновки в Razor и Выражения Razor. В этой и следующих статьях будет показано, как применять другие средства для создания и визуализации представлений Razor. Вы также узнаете, каким образом настраивать механизм Razor.

Пример приложения

Мы создали новый проект MVC по имени WorkingWithRazor с использованием шаблона Empty (Пустой) и отметкой флажка MVC в разделе Add folders and core references for (Добавить папки и основные ссылки для). В проект добавлен контроллер Home, код которого показан в примере:

using System;
using System.Web.Mvc;

namespace WorkingWithRazor.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            string[] names = { "Яблоко", "Апельсин", "Груша" };
            return View(names);
        }
    }
}

Кроме того, в папке /Views/Home создано представление по имени Index.cshtml. Содержимое этого файла представления приведено ниже:

@model string[]
@{
    ViewBag.Title = "Index";
}

<h2>Названия фруктов</h2>
@foreach (string fruit in Model)
{
    <br /><span><b>@fruit</b></span>
}

Визуализация представлений Razor

Для улучшения показателей производительности механизм визуализации Razor компилирует представления, созданные в приложении. Представления транслируются в классы C#, после чего компилируются; именно поэтому настолько легко включать фрагменты кода C#. Полезно просмотреть исходный код, сгенерированный для представлений Razor, поскольку это позволит лучше понять многие средства Razor.

Представления в приложении MVC не компилируются вплоть до момента запуска приложения, так что для просмотра классов, созданных Razor, понадобится запустить приложение и перейти к действию /Home/Index. Начальный запрос к приложению MVC инициирует процесс компиляции для всех представлений. Вывод произведенного запроса показан на рисунке ниже:

Простой пример с использованием представления Razor

По соглашению классы, сгенерированные из файлов представлений, сначала сохраняются на диске в виде файлов кода C# и затем компилируются, а это значит что можно просмотреть операторы C#, относящиеся к представлению. Сгенерированные файлы расположены в папке:

C:\Users\Имя пользователя\AppData\Local\Temp\Temporary ASP.NET Files

в системах Windows 7 и Windows 8.

Нахождение файла кода, сгенерированного для конкретного представления, требует определенного внимания. Обычно имеется множество папок с загадочными именами, к тому же имена файлов .cs не соответствуют именам содержащихся в них классов. К примеру, класс, сгенерированный для представления из примера выше, был обнаружен в файле по имени "App_Web_o0mtztby.0.cs" внутри папки "root\321dfcd8\64cbe7dd\". В примере ниже приведен слегка упорядоченный код этого класса:

namespace ASP {
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Web;
    using System.Web.Helpers;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.WebPages;
    using System.Web.Mvc;
    using System.Web.Mvc.Ajax;
    using System.Web.Mvc.Html;
    using System.Web.Routing;
    using WorkingWithRazor;
    
    
    public class _Page_Views_Home_Index_cshtml : System.Web.Mvc.WebViewPage<string[]> {
        
        public _Page_Views_Home_Index_cshtml() {
        }
        
        protected ASP.global_asax ApplicationInstance {
            get {
                return ((ASP.global_asax)(Context.ApplicationInstance));
            }
        }
        
        public override void Execute() {
  
            ViewBag.Title = "Index";

            BeginContext("~/Views/Home/Index.cshtml", 52, 31, true);

            WriteLiteral("\r\n\r\n<h2>Названия фруктов</h2>\r\n");

            EndContext("~/Views/Home/Index.cshtml", 52, 31, true);

            foreach (string fruit in Model)
            {
                BeginContext("~/Views/Home/Index.cshtml", 120, 10, true);
                WriteLiteral("    <br />");
                EndContext("~/Views/Home/Index.cshtml", 120, 10, true);

                BeginContext("~/Views/Home/Index.cshtml", 130, 9, true);
                WriteLiteral("<span><b>");
                EndContext("~/Views/Home/Index.cshtml", 130, 9, true);

                BeginContext("~/Views/Home/Index.cshtml", 145, 13, true);
                WriteLiteral("</b></span>\r\n");
                EndContext("~/Views/Home/Index.cshtml", 145, 13, true);
            }
        }
    }
}

Первым делом обратите внимание, что класс является производным от WebViewPage<T>, где T - это тип модели (WebViewPage<string[]> в рассматриваемом примере). Подобным образом поддерживаются строго типизированные представления. Кроме того, взгляните на имя сгенерированного класса - _Page_Views_Home_Index_cshtml. Как видите, в имени класса закодирован путь к файлу представления. Таким способом Razor отображает запросы к представлениям на экземпляры скомпилированных классов.

В методе Execute() производится обработка операторов и элементов представления. Фрагменты кода, предваренные символом выражаются непосредственно как операторы C#. Элементы HTML обрабатываются с помощью метода WriteLiteral(), который записывает содержимое параметра в результат в том виде, как он задан. Это отличается от метода Write(), который используется для переменных C# и кодирует строковые значения для безопасного применения на HTML-странице.

Методы Write() и WriteLiteral() записывают содержимое в объект TextWriter. Это тот же самый объект, который передается методу IView.Render(), как было показано в предыдущей статье. Цель скомпилированного представления Razor заключается в генерации статического и динамического содержимого и отправка его клиенту через TextWriter. Об этом полезно помнить, когда будут рассматриваться вспомогательные методы HTML.

Конфигурирование местоположений для поиска представлений

При поиске представления механизм визуализации Razor следует стандартному соглашению. Например, в случае запроса представления Index, ассоциированного с контроллером Home, механизм Razor просматривает следующий список представлений:

~/Views/Home/Index.cshtml
~/Views/Home/Index.vbhtml
~/Views/Shared/Index.cshtml
~/Views/Shared/Index.vbhtml

Как вам уже известно, Razor в действительности не ищет файлы представлений на диске, поскольку они уже скомпилированы в классы C#. Механизм Razor ищет скомпилированные классы, соответствующие этим представлениям. Файлы .cshtml - это шаблоны, содержащие операторы C#, а файлы .vbhtml содержат операторы Visual Basic.

Чтобы изменить файлы представлений, которые ищет Razor, можно создать подкласс класса RazorViewEngine. Указанный класс является реализацией интерфейса IViewEngine в Razor. Он построен на серии базовых классов, которые определяют набор свойств, указывающих на то, какие файлы представлений будут искаться. Эти свойства описаны в таблице ниже:

Свойства механизма визуализации Razor, связанные с поиском представлений
Свойство Описание Стандартное значение
ViewLocationFormats
MasterLocationFormats
PartialViewLocationFormats

Местоположения для поиска представлений, частичных представлений и компоновок

~/Views/{1}/{0}.cshtml
~/Views/{1}/{0}.vbhtml
~/Views/Shared/{0}.cshtml
~/Views/Shared/{0}.vbhtml
AreaViewLocationFormats AreaMasterLocationFormats
AreaPartialViewLocationFormats

Местоположения для поиска представлений, частичных представлений и компоновок для области данных

~/Areas/{2}/Views/{1}/{0}.cshtml
~/Areas/{2}/Views/{1}/{0}.vbhtml
~/Areas/{2}/Views/Shared/{0}.cshtml
~/Areas/{2}/Views/Shared/{0}.vbhtml

Приведенные свойства предшествовали появлению Razor, потому каждый набор из трех свойств имеет те же самые значения. Каждое свойство является массивом строк, которые выражены с использованием формата составных строк.

Ниже перечислены значения параметров, соответствующие заполнителям:

Для изменения просматриваемых местоположений вы создаете новый класс, производный от RazorViewEngine, и устанавливаете желаемым образом значения одного или нескольких свойств из таблицы.

В целях демонстрации изменения местоположений для поиска в проект добавлена папка Infrastructure, а в ней создан файл класса по имени CustomLocationViewEngine.cs, содержимое которого показано в примере ниже:

using System.Web.Mvc;

namespace WorkingWithRazor.Infrastructure
{
    public class CustomLocationViewEngine : RazorViewEngine
    {
        public CustomLocationViewEngine()
        {
            ViewLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml", 
                "~/Views/Common/{0}.cshtml" };
        }
    }
}

Для свойства ViewLocationFormats указано новое значение. Новый массив содержит элементы только для файлов .cshtml. Вдобавок местоположение, в котором ищутся разделяемые представления, изменено на Views/Common вместо Views/Shared. Производный механизм визуализации регистрируется с использованием коллекции ViewEngines.Engines в методе Application_Start() внутри файла Global.asax, как показано в примере ниже:

using System.Web.Mvc;
using System.Web.Routing;
using WorkingWithRazor.Infrastructure;

namespace WorkingWithRazor
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);

            ViewEngines.Engines.Clear();
            ViewEngines.Engines.Add(new CustomLocationViewEngine());
        }
    }
}

Вспомните, что активатор действий обращается к каждому механизму визуализации, чтобы проверить, может ли быть найдено представление. К тому времени, когда появится возможность добавить представление в эту коллекцию, она уже будет содержать стандартный механизм визуализации Razor. Во избежание конфликта с этой реализацией мы вызываем метод Clear() для удаления любых уже зарегистрированных механизмов, а затем метод Add() для регистрации специальной реализации.

Чтобы продемонстрировать изменения, внесенные в местоположения для поиска, была создана папка /Views/Common, а нее добавлен файл представления по имени List.cshtml. Содержимое этого файла приведено в примере ниже:

@{
    ViewBag.Title = "List";
}

<em>Это файл представления <b>~/Views/Common/List.cshtml</b></em>

Чтобы отобразить это представление, в контроллер Home добавлен новый метод действия, как показано в примере ниже:

using System;
using System.Web.Mvc;

namespace WorkingWithRazor.Controllers
{
    public class HomeController : Controller
    {
        // ...

        public ViewResult List()
        {
            return View();
        }
    }
}

Если запустить приложение и перейти на URL вида /Home/List, то для обнаружения файла представления List.cshtml в папке /Views/Common будут использоваться специальные местоположения:

Результат применения специальных местоположений в механизме визуализации

Добавление динамического содержимого к представлению Razor

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

Его противоположностью является статическое содержимое вроде HTML-разметки, которое создается во время написания кода приложения и одинаково для всех запросов. Добавлять динамическое содержимое к представлениям можно различными способами которые описаны ниже:

Встраиваемый код Razor

Используется для небольших и независимых порций логики представления, таких как операторы if и foreach. Это фундаментальное средство для создания динамического содержимого в представлениях, на котором построен ряд других подходов.

Вспомогательные методы HTML

Используются для генерации одиночных HTML-элементов или небольших их коллекций, обычно основанных на значениях модели или данных представления. Инфраструктура MVC Framework включает множество полезных вспомогательных методов HTML, и можно также легко создавать собственные методы подобного рода. Любой метод, возвращающий объект MvcHtmlString, может служить вспомогательным методом HTML.

Разделы компоновки

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

Частичные представления

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

Дочерние действия

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

Три последних приема будут рассмотрены в последующих статьях.

Пройди тесты
Лучший чат для C# программистов