Сокращение дублирования кода с помощью HTML-помощников - PullRequest
0 голосов
/ 24 января 2012

У меня есть довольно простое веб-приложение для аудита данных, написанное на ASP MVC, которое фактически имеет два представления одной и той же модели для разных целей.

  1. Представление агента - форма, заполненная лицом, подтверждающим информацию и информацию. Каждое поле в форме в этом представлении имеет 3 подполя:

    а. Исходное значение - значение из базы данных до вызова

    б. Новое значение - значение, указанное человеком в телефоне, если оно отличается от оригинала.

    с. Действие - общее указание на то, что произошло

  2. QC View - форма, заполненная кем-то, кто просматривает работу, выполненную в представлении Agent. Каждое поле в форме в этом представлении имеет 5 подполей:

    а. Исходное значение - то же, что и выше

    б. Значение агента - значение, указанное агентом в пункте 1b выше.

    с. Значение КК - исправленное «Новое значение», если значение, указанное агентом, неверно.

    * * 1 022 д. Действие агента - то же, что и выше, за исключением чтения только в этом представлении

    е. Действие QC - исправленное «Новое действие», если оно неправильно выбрано агентом.

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

// Agent controls
public static MvcHtmlString AuditControl(this HtmlHelper htmlHelper, string id, string fieldLabel, MvcHtmlString editControl, string cssClass)
public static MvcHtmlString AuditControl(this HtmlHelper htmlHelper, string id, string fieldLabel, string editControl, string cssClass)
public static MvcHtmlString AuditControl<COMPLEX>(this HtmlHelper htmlHelper, string id, string fieldLabel, string cssClass) where COMPLEX : AbstractComplex, new()

// QC controls
public static MvcHtmlString ReviewControl(this HtmlHelper htmlHelper, string id, string fieldLabel, MvcHtmlString editControl, string cssClass)
public static MvcHtmlString ReviewControl(this HtmlHelper htmlHelper, string id, string fieldLabel, string editControl, string cssClass)
public static MvcHtmlString ReviewControl<COMPLEX>(this HtmlHelper htmlHelper, string id, string fieldLabel, string cssClass) where COMPLEX : AbstractComplex, new()

Где третьи реализации обрабатывают более сложные поля, состоящие из нескольких частей данных (таких как полное имя, адрес и т. Д.).

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

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

Это разумная ситуация, чтобы нарушить разделение интересов, как было разработано MVC, или есть более подходящее решение?

Ответы [ 2 ]

3 голосов
/ 24 января 2012

Поскольку вы используете MVC3, я бы рекомендовал использовать дочерние действия для подполей:

http://haacked.com/archive/2009/11/18/aspnetmvc2-render-action.aspx

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

0 голосов
/ 14 февраля 2012

Мне удалось реализовать (мою интерпретацию) совет, предоставленный @SoWeLie, довольно просто. Он включал в себя создание новой модели для размещения расширенного набора возможных свойств элемента управления и нового представления для каждого отдельного набора элементов управления (один для аудита и один для просмотра). Проблема с этим заключалась в том, что получившийся API представления был уродлив:

@Html.RenderAction("DrawControl", new { id = "ID" ... })
// Repeated for all of the overloads of DrawControl

и каждое действие контроллера содержало что-то вроде:

public ActionResult DrawControl(string id, ...)
{
    // FieldControl being the name of my Model
    var viewModel = new FieldControl() { ID = id, ... };
    if (shouldRenderAudit)
        return PartialView("AuditControl", viewModel);
    else
        return PartialView("ReviewControl", viewModel);

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

@functions {
    public string DrawControl(string id, ...) 
    {
        return Html.Render("DrawControl", new { id = "ID" });
    }
    // Repeated for all of the overloads of DrawControl
}

@DrawControl("ID", ...)

С тем же действием контроллера. Проблема с этим (игнорирование того факта, что представление вообще имело функции) заключалась в том, что блок @functions необходимо было включать в любое представление, которое хотело получить выгоду от их использования (которое в настоящее время составляет только 2, но скоро будет достаточно всплывать до 5). и кто знает, что мой предшественник собирается с этим делать). Я быстро переработал код снова, на этот раз, чтобы вернуть помощников (обычно сохраняя представления, модель и изменения контроллера), и в итоге получил следующее:

Вид:

@(Html.DrawComplexControl<ProviderName>("id", ...))
@Html.DrawSimpleControl("id", ...)

Контроллер:

// One common action that is used to determine which control should be drawn
public ActionResult DrawControl(FieldControl model)
{
    if (shouldRenderAudit)
        return PartialView("AuditControl", model);
    else
        return PartialView("ReviewControl", model);
}

Помощник:

public static MvcHtmlString DrawControl(this HtmlHelper htmlHelper, string id, ...)
{
    var model = new FieldControl() { ID = id, ... };

    return htmlHelper.Action("DrawControl", model);
}

public static MvcHtmlString DrawSimpleControl(this HtmlHelper htmlHelper, string id, ...)
{
    return DrawSimpleControl(htmlHelper, id, ...);
}
public static MvcHtmlString DrawSimpleControl(this HtmlHelper htmlHelper, string id, ...)
{
    // Set some defaults to simplify the API
    return DrawControl(htmlHelper, id, ...);
}

public static MvcHtmlString DrawComplexControl<T>(this HtmlHelper htmlHelper, string id, ...) where T : AbstractComplex, new()
{
    // Build the required controls based on `T`

    return DrawControl(htmlHelper, id, ...);
}

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

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

Надеюсь, мои головные боли помогут кому-то еще в будущем.

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