Статьи

Сохраняйте свою логику шаблона в шаблоне

Недавно я наткнулся на этот пост в блоге . К сожалению, у Джонатана Холланда все совершенно неправильно. Прежде всего, статья говорит только о представлении во всех смыслах. И в современных сложных приложениях презентация может легко включать небольшую логику, чтобы придать ей «липкость», которую любят маркетинговые типы. Ваше ядро, логика приложения определенно не должны жить на веб-страницах ASP.NET. Или желательно даже не в веб-проекте вообще. Но ваша логика шаблона может очень легко жить в шаблоне и, если это вообще возможно, должна жить больше в шаблоне ASPX, чем в CodeBehind.

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

  1. Если вы полагаетесь на CodeBehind, чтобы делать все, вы не можете ничего изменить в повторителе без перекомпиляции и повторного развертывания всего приложения или, по крайней мере, только основной DLL. Это означает, что внесение незначительного изменения в текст приведет к полной переработке, сбросу всех существующих сеансов и заставлению пользователей ждать в цикле раскрутки ASP.NET. Это также означает, что для срочных исправлений требуется разработчик с Visual Studio и доступ ко всему источнику проекта. Принимая во внимание, что я, как известно, в крайнем случае вставлял экстренное исправление на уровне шаблона, используя блокнот на рабочем сервере.

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

  2. Все элементы управления сервером создают некоторые неприятные издержки с нескольких точек зрения. Во-первых, у них есть ViewState. Во-вторых, они являются серверными элементами управления, поэтому они будут проанализированы и загружены в дерево элементов управления. [РЕДАКТИРОВАТЬ: Как г-н Холланд указывает ниже, это немного неясно и частично неправильно. HtmlControls также появляются в дереве элементов управления, но они намного «легче», чем настоящие WebControls — без модели событий и без ViewState.]

  3. На мой взгляд, гораздо проще разобраться в HTML, когда он находится в шаблоне в стиле HTML, а не полностью отключен на стороне сервера.

  4. Если вы работаете с отдельной командой дизайнеров, они все равно дадут вам HTML для таких вещей. Зачем пытаться перевести его в скомпилированный серверный код? Более того, большинство хороших дизайнеров в наши дни, по крайней мере, в некоторой степени могут обрабатывать код в стиле шаблонов ASPX — они знают, что не нужно заходить на <% #%> землю, и, как правило, могут «прочитать» эти вещи, если это будет необходимо. Более умные могут даже подобрать достаточно синтаксиса DataBinding, чтобы быть почти полезным.

Теперь вы можете спросить — что мне делать, когда мне нужна условная логика в шаблоне. Я, конечно, должен использовать Codebehind, чтобы привести это в действие? И, по крайней мере, для лёгкой логики, которая нужна в шаблоне, ответ — нет, все это можно сделать встроенным. Даже для более сложной логики можно делать вызовы непосредственно из шаблона для более глубоких сервисов приложений, если этого требуют требования. Давайте используем немного более сложную версию rptrComment мистера Холланда, связанную с классом комментариев со следующими полями:

  • Идентификатор Guid
  • строка AuthorName
  • Uri AuthorWeb
  • Строка AuthorEmail
  • строка AuthorIpAddress
  • строка CommentText
  • DateTime CommentTimeStamp
  • bool IsSuppressed

Теперь, если следовать шаблонной логике, ваши требования утверждают следующее:

  • Все пользователи должны видеть поля AuthorName, AuthorWeb и CommentText и CommentTimeStamp для неподавленных комментариев.
  • Владельцы статьи и администраторы должны видеть вышеуказанные поля, а также AuthorEmail и AuthorIpAddress. Кроме того, они увидят все комментарии, даже подавленные.
  • Администраторы должны иметь возможность подавлять комментарии.

Теперь это можно сделать с помощью множества серверных элементов управления и кода. Или можно просто вставить все элементы слоя отображения в шаблон и вызвать оттуда внутренние классы, такие как ваш SecurityHelper:


<asp:Repeater runat="server" ID="CommentsRepeater" DataSource='<%# GetComments %>'>
    <HeaderTemplate>
        <ul class="commentcontainer">
    </HeaderTemplate>
    <FooterTemplate>
        </ul>
    </FooterTemplate>
    <ItemTemplate>
        <li runat="server" visible='<%# ((SitePoint.Core.Entities.Content.ArticleComment)Container.DataItem).IsSuppressed && !SitePoint.Core.Security.SecurityHelper.CanSeeCommentInfo(User) ? false : true %>'>
           <p><%# string.IsNullOrEmpty(((SitePoint.Core.Entities.Content.ArticleComment)Container.DataItem).CommentText) ? "No comment . . ." : ((SitePoint.Core.Entities.Content)Container.DataItem).CommentText %></p>
           <p>
               By : <a href='<%# ((SitePoint.Core.Entities.Content.ArticleComment)Container.DataItem).AuthorWeb.ToString() %>'><%# ((SitePoint.Core.Entities.Content.ArticleComment)Container.DataItem).AuthorName %></a> on <%# ((SitePoint.Core.Entities.Content.ArticleComment)Container.DataItem).CommentTimeStamp.ToShortDateString() %> at <%# ((SitePoint.Core.Entities.Content.ArticleComment)Container.DataItem).CommentTimeStamp.ToShortTimeString() %></p>
           <p runat="server" visible='<%# SitePoint.Core.Security.SecurityHelper.CanSeeCommentInfo(User) %>'>
                <a href='mailto:<%#((SitePoint.Core.Entities.Content.ArticleComment)Container.DataItem).AuthorEmail %>'><%# ((SitePoint.Core.Entities.Content.ArticleComment)Container.DataItem).AuthorEmail %></a> 
                • 
                IP: <%# ((SitePoint.Core.Entities.Content.ArticleComment)Container.DataItem).AuthorIpAddress %>
                <asp:LinkButton runat="server" ID="SuppressCommentButton" CommandArgument='<%# ((SitePoint.Core.Entities.Content.ArticleComment)Container.DataItem).Id %>' Text='[Suppress Comment]' Visible='<%# !((SitePoint.Core.Entities.Content.ArticleComment)Container.DataItem).IsSuppressed && SitePoint.Core.Security.CanSuppressComment(User) %>' OnClick="DoSuppressComment" />
           </p>
        </li>
    </ItemTemplate>
</asp:Repeater>

Теперь, на первый взгляд, это выглядит ужасно сложно. Но на самом деле это не так — 90% кода DataBinding преобразует ваш DataItem контейнера [RepeaterItem] в его естественный класс: ((SitePoint.Core.Entities.Content.ArticleComment) Container.DataItem). Как только кто-то смотрит мимо этого (или просто добавляет <% @ Reference%>, что я не хочу делать), он становится намного более удобочитаемым — на самом деле простой шаблон ASPX, не более и не менее. Кроме того, это гораздо более податливее, чем помещать всю эту логику шаблонов в ваш код, поскольку вы можете настраивать, перезагружать, настраивать и перезагружать без перестройки.

Один вопрос, который вы, возможно, задаете: «Почему все это приведение вместо вызова DataBinder.Eval? Ну, потому что, когда вы знаете, какой класс предоставляет ваш DataItem, есть более чем несколько преимуществ:

  1. Intellisense обычно работает с этой тактикой. Здесь я должен отметить, что использование такого большого количества DataBinding может в некоторых случаях привести в готовность дизайнера, и его не всегда нужно слушать.
  2. DataBinder.Eval намного медленнее, поскольку использует отражение (см. Примечание на этой странице ).
  3. Мы можем вызывать методы по нашей теперь строго типизированной ссылке. DataBinder.Eval () позволяет только выводить строки.

Второй вопрос, который вы можете задать: «Почему вы используете runat = ‘server’ в некоторых из этих HTML-тегов?» Причина в том, что они стали вашими контейнерами для частей сообщения, которые вы хотите подавить. Во-первых, в элементе <li> мы скрываем все это, если ArticleComment.IsSuppressed и пользователь не имеет необходимых привилегий. Позже мы добавили <p>, чтобы содержать административные функции для данного поста.

В интересах полного раскрытия, я должен указать на одну существенную проблему с этим подходом: рефакторинг. Современные инструменты, такие как Resharper и, в меньшей степени, Visual Studio 2005+, содержат довольно неплохие инструменты для автоматизации именования и изменения местоположения в коде. Поскольку код в шаблонах ASPX не компилируется до времени выполнения, эти инструменты часто упускают этот угол. Поэтому, если у вас большая работа по рефакторингу, вам нужно будет дотронуться до каждого файла шаблона, чтобы убедиться, что он по-прежнему совместим с серверной частью. Конечно, если у вас большая работа по рефакторингу, вы, вероятно, все равно будете касаться большей части внешнего кода, так что это, вероятно, не так уж и плохо.

Помните, дети, логика шаблона в шаблоне — это хорошо. И не позволяй злому человеку сказать тебе иначе.

пнуть его на DotNetKicks.com