Приходилось ли Вам сталкиваться с долгоиграющими проектами на ASP.NET?
Может быть Вы сейчас над таким проектом как раз и трудитесь?
Если да, то вы скорее всего сталкивались со сложно переплетенным набором событий, логики и валидаций внутри классов страниц.
Эта статья рассказывает о том как можно упростить жизнь на таких проектах используя все тот же шаблон Model-View-Presenter.
Предположу что Вы знакомы с шаблоном MVC и не буду в этой статье на нем подробно останавливаться.
Я попробую сопоставить MVC с теми возможностями которые предоставляет классический ASP.NET:
1. Routing — обработка запросов
Дерево папок сайта в большинстве случаев может служить тем же целям.
2. View и PartialView — представление данных
Класс Page и соответственно UserControl замечательно подходят для этих целей.
3. Model — данные
Классы моделей скорее всего присутствуют в Вашей системе в каком либо виде.
4. Presenter — клей между данными и представлением
Вот этого типа объектов ASP.NET к сожалению не предусматривает.
Реализацией презентера мы сейчас и будем заниматься.
Назначение презентера — подготовить данные для показа пользователю и обработать введенные им данные.
Назначение представления — показать данные пользователю и получить данные от пользователя.
Существуют разные мнения на счет того, насколько умным может быть представление и презентер.
Так как единого мнения нет, то я предпочитаю исходить из эффективности реализации которая зависит от окружения. В данном случае от имеющейся библиотеки ASP.NET которая обладает широким набором достаточно умных элементов управления которые хотелось бы использовать в полной мере, раз уж такая возможность присутствует.
Наконец приступаем к реализации.
Создаем два класса PageView и UserControlView:
Класс PageView абстрактный и предполагает самостоятельное создание презентера в потомке.
Класс UserControlView обладает единственным свойством — Presenter которое инициализируется родителем на котором пользовательский элемент располагается.
Предполагается что презентер страницы содержит презентеры своих пользовательских элементов.
Пример реализации списка и его элемента:
List.aspx
List.aspx.cs
Details.aspx
Detalis.aspx.cs
Кстати, прошу заметить, ViewState на страницах выключен.
Полностью пример можно скачать здесь.
Достоинства такого подхода:
1. Код страницы становится значительно проще
2. Можно писать модульные тесты на классы презентеров
3. Можно использовать презентеры для реализации различных интерфейсов (web, win, mobile)
4. Можно использовать совместно с лапшекодом и модернизировать приложение постепенно
Надеюсь мой опыт будет Вам полезен.
Может быть Вы сейчас над таким проектом как раз и трудитесь?
Если да, то вы скорее всего сталкивались со сложно переплетенным набором событий, логики и валидаций внутри классов страниц.
Эта статья рассказывает о том как можно упростить жизнь на таких проектах используя все тот же шаблон Model-View-Presenter.
Предположу что Вы знакомы с шаблоном MVC и не буду в этой статье на нем подробно останавливаться.
Я попробую сопоставить MVC с теми возможностями которые предоставляет классический ASP.NET:
1. Routing — обработка запросов
Дерево папок сайта в большинстве случаев может служить тем же целям.
2. View и PartialView — представление данных
Класс Page и соответственно UserControl замечательно подходят для этих целей.
3. Model — данные
Классы моделей скорее всего присутствуют в Вашей системе в каком либо виде.
4. Presenter — клей между данными и представлением
Вот этого типа объектов ASP.NET к сожалению не предусматривает.
Реализацией презентера мы сейчас и будем заниматься.
Назначение презентера — подготовить данные для показа пользователю и обработать введенные им данные.
Назначение представления — показать данные пользователю и получить данные от пользователя.
Существуют разные мнения на счет того, насколько умным может быть представление и презентер.
Так как единого мнения нет, то я предпочитаю исходить из эффективности реализации которая зависит от окружения. В данном случае от имеющейся библиотеки ASP.NET которая обладает широким набором достаточно умных элементов управления которые хотелось бы использовать в полной мере, раз уж такая возможность присутствует.
Наконец приступаем к реализации.
Создаем два класса PageView и UserControlView:
public abstract class PageView<T>: Page where T : class { private T _presenter; public T Presenter { get { return _presenter ?? (_presenter = CreatePresenter()); } } protected abstract T CreatePresenter(); } public class UserControlView<T> : UserControl { public T Presenter { get; set; } }
Класс PageView абстрактный и предполагает самостоятельное создание презентера в потомке.
Класс UserControlView обладает единственным свойством — Presenter которое инициализируется родителем на котором пользовательский элемент располагается.
Предполагается что презентер страницы содержит презентеры своих пользовательских элементов.
Пример реализации списка и его элемента:
List.aspx
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="List.aspx.cs" Inherits="WebApplication1.Client.List" EnableViewState="false" %> <%@ Register TagPrefix="ctl" TagName="Item" Src="Details.ascx" %> <asp:Content ContentPlaceHolderID="MainContent" runat="server"> <asp:Repeater ID="ctlList" runat="server" > <ItemTemplate> <ctl:Item runat="server" Presenter="<%# Container.DataItem %>" /> </ItemTemplate> </asp:Repeater> </asp:Content>
List.aspx.cs
public partial class List : PageView<ListPresenter> { protected override ListPresenter CreatePresenter() { return new ListPresenter(); } protected override void OnLoad(System.EventArgs e) { base.OnLoad(e); ctlList.DataSource = Presenter.Items; ctlList.DataBind(); } }
Details.aspx
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Details.ascx.cs" Inherits="WebApplication1.Client.Details" %> <asp:TextBox runat="server" Text="<%# Presenter.Model.FirstName %>" OnTextChanged="OnFirstNameChanged" /> <asp:TextBox runat="server" Text="<%# Presenter.Model.LastName %>" OnTextChanged="OnLastNameChanged" /> <asp:Button runat="server" Text="Save" OnClick="OnSave" /> <br />
Detalis.aspx.cs
public partial class Details : UserControlView<DetailsPresenter> { protected void OnFirstNameChanged(object sender, System.EventArgs e) { Presenter.Model.FirstName = ((TextBox) sender).Text; } protected void OnLastNameChanged(object sender, System.EventArgs e) { Presenter.Model.LastName = ((TextBox)sender).Text; } protected void OnSave(object sender, System.EventArgs e) { Presenter.Save(); } }
Кстати, прошу заметить, ViewState на страницах выключен.
Полностью пример можно скачать здесь.
Достоинства такого подхода:
1. Код страницы становится значительно проще
2. Можно писать модульные тесты на классы презентеров
3. Можно использовать презентеры для реализации различных интерфейсов (web, win, mobile)
4. Можно использовать совместно с лапшекодом и модернизировать приложение постепенно
Надеюсь мой опыт будет Вам полезен.
