Создание строго типизированных ссылок на контент в ASP .Net MVC с помощью шаблонов T4

Изменить: я сделал некоторые исправления к этому сообщению в ** последующем сообщении . **

Я прочитал интересную статью Дэвида Эббо о создании строго типизированных помощников для ASP .Net MVC с использованием шаблонов T4 . Я использовал ASP .Net MVC в недавнем проекте, и хотя мне действительно нравятся фреймворк (и тестируемость), использование строковых литералов сделало серьезные изменения трудными и подверженными ошибкам.

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

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

Чего мы пытаемся достичь

В тот момент, когда мы хотим сослаться на статический контент, такой как таблица стилей, нам нужно сделать следующее.

<head runat="server">
  <link href="<%= Url.content("~/Content/Site.css") %>" rel="stylesheet" type="text/css" />
</head>

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

Я хотел бы сделать следующее.

<head runat="server">
  <link href="<%= Url.content(Content.Site) %>" rel="stylesheet" type="text/css" />
</head>

Обратите внимание, что я не заменяю вызов Url.Content — это на самом деле экземпляр класса UrlHelper, экземпляр которого создается с правильным RequestContext — мы не можем его заменить. Мы могли бы использовать методы расширения, но синтаксис намного лучше, используя свойства и статические классы.

Использование шаблонов T4

Шаблоны T4 встроены в Visual Studio и невероятно полезны во всех видах сценариев генерации кода. По сравнению с моделью генерации кода CodeDOM , я считаю, что концепция шаблонов T4 проще, но гораздо сложнее в реализации.

Если вы никогда ранее не использовали шаблоны T4, имейте в виду, что Visual Studio рассматривает их как обычные текстовые файлы.  Это означает, что у вас нет intellisense или завершения кода . Однако есть целый ряд плагинов для Visual Studio, которые добавляют эту функциональность — я использовал T4 Editor от Tangible Engineering .

Давайте напишем некоторый код

Первое, что нам нужно сделать, это найти проект Visual Studio, в котором находится наш файл шаблона T4. Сначала это может показаться немного запутанным, но это имеет смысл — мы могли бы использовать этот шаблон для генерации чего-либо — в этом случае я знать, что файл шаблона будет находиться в корневой папке веб-проекта. Код здесь может показаться довольно сложным, но если вы уже создали плагин для Visual Studio, это должно быть довольно просто. Большая часть этого раздела принадлежит Дэвиду Эббо.

<#
    // First just get the visual studio project this file belongs to
    var dte = (DTE)((IServiceProvider)Host).GetService(typeof(SDTE));
    Project project = GetProjectContainingT4File(dte, Path.GetFileName(Host.TemplateFile));

    GenerateLinksForFolder(project, "Scripts");
    GenerateLinksForFolder(project, "Content");
#>

Получив объект проекта, просто выберите папку, для которой вы хотите создать статические классы.

void RecursivelyIterateThroughFolder(ProjectItem item, string currentPath)
    {
        var fileNameWithoutExtension = Sanitize(Path.GetFileNameWithoutExtension(item.Name));
        var fileName = Path.GetFileName(item.Name);
        if (item.ProjectItems.Count == 0)
        {
#>
            public const string <#=fileNameWithoutExtension#> = "<#=currentPath + fileName#>";
<#+
        }
        else
        {
#>
            public static class <#=fileNameWithoutExtension#>
            {
<#+
            foreach(ProjectItem childItem in item.ProjectItems)
            {
                RecursivelyIterateThroughFolder(childItem, currentPath + fileName + "/");
            }
#>
            }
<#+
        }
    }

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

public static class Content
{
    public static class Images
    {
        public const string Add = "~/Content/Content/Images/Add.png";
        public const string Check = "~/Content/Content/Images/Check.png";
        public const string Delete = "~/Content/Content/Images/Delete.png";
        public const string Edit = "~/Content/Content/Images/Edit.png";
        public const string FolderBackground = "~/Content/Content/Images/FolderBackground.gif";
        public const string FolderBottom = "~/Content/Content/Images/FolderBottom.gif";
        public const string FolderTop2 = "~/Content/Content/Images/FolderTop2.gif";
        public const string Guy = "~/Content/Content/Images/Guy.png";
        public const string Mail = "~/Content/Content/Images/Mail.png";
        public const string Papers = "~/Content/Content/Images/Papers.jpg";
    }

    public const string Site = "~/Content/Content/Site.css";
}

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

<img src="<%= Url.Content(Content.Images.Edit) %>" alt="Edit" />

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

Есть 2 проблемы с этим подходом:

  1. Созданный контент создается только при сохранении шаблона T4. Я попытался добавить событие после сборки, чтобы сгенерировать содержимое при компиляции, но мое использование хост-объекта вызвало проблемы. Мне нужно попытаться найти аккуратный способ сделать это.
  2. При публикации сайта мы явно не хотим включать файл шаблона — я хочу попробовать и посмотреть, какие у меня есть варианты. Это может быть так же просто, как исключение файла шаблона при выполнении развертывания.

Author: admin

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *