Приходилось ли Вам сталкиваться с долгоиграющими проектами на 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. Можно использовать совместно с лапшекодом и модернизировать приложение постепенно
Надеюсь мой опыт будет Вам полезен.