Текст книги "Asp.net mvc framework"
Автор книги: Гайдар Магдануров
сообщить о нарушении
Текущая страница: 7 (всего у книги 17 страниц)
RedirectResult предназначен для того, чтобы возвратить результат пользователю в виде перенаправления на заданный адрес URL. У RedirectResult есть только одно свойство, которое инициализируется через конструктор, – Url, оно указывает строку адреса, на которую будет перенаправлен пользователь в ответ на запрос. Контроллеры MVC содержат стандартный метод Redirect, который формирует ответ в виде RedirectResult. В следующем фрагменте приведено действие, результатом которого является перенаправление пользователя на сайт http://www.asp.net/mvc/:
public ActionResult GetAspNetSite()
{
return Redirect("http://www.asp.net/mvc/");
}
RedirectToRouteResult выполняет схожую по смыслу с RedirectResult логику, но перенаправление вызова RedirectToRouteResult производится только на основании маршрутов таблицы маршрутизации. RedirectToRouteResult имеет два конструктора, с разным числом параметров, всего параметров два:
□ routeName – указывает наименование маршрута, на который нужно выполнить перенаправление запроса;
□ routeValues – указывает набор значений параметров маршрута типа RouteValueDictionary, с помощью которых производится поиск маршрута и выполняется перенаправление.
Для упрощения работы с RedirectToRouteResult механизм MVC реализует для контроллеров, наряду с методами RedirectToRoute, набор стандартных методов RedirectToAction, которые призваны облегчить формирование перенаправления вызова на другие действия или контроллеры. Например, следующий фрагмент кода перенаправляет вызов из текущего действия в действие Index текущего контроллера:
return RedirectToAction(«Index»);
При использовании RedirectToAction можно указывать и контроллер, в который требуется перенаправить вызов, кроме того, можно указать набор значений параметров маршрута типа RouteValueDictionary. Следующий пример кода перенаправит вызов на действие Index контроллера AccountController:
return RedirectToAction("Index", "Account");
ContentResult
ContentResult – это весьма простая реализация ActionResult, которая предназначена для того, чтобы в ответ на запрос передавать любой пользовательский строковый набор данных. Для реализации логики у ContentResult есть три свойства:
□ ContentType – MIME-тип передаваемых в ответ на запрос данных;
□ ContentEncoding – кодировка данных;
□ Content – строка данных для передачи в ответ на запрос.
Благодаря ContentResult разработчик получает возможность генерировать ответы на запросы в любом виде, который можно представить в виде строки текста. Этот тип ActionResult может быть полезен при работе с механизмом RenderAction. RenderAction – это часть библиотеки MVCContrib, которая содержит расширения MVC Framework, не вошедшие в основной функционал. RenderAction позволяет представлению вывести в месте вызова результат выполнения действия. При таком применении результат типа ContentResult подходит более всего. Для упрощения контроллеры содержат специальный метод Content, который возвращает значение типа ContentResult.
EmptyResult
Последний из рассмотренных стандартных вариантов ActionResult – это EmptyResult. Этот механизм предназначен для того, чтобы в ответ на запрос не возвращать ничего. Переопределенный в EmptyResult метод ExecuteResult не содержит ни строчки кода.
Создание своей реализации ActionResult
Важной особенностью механизма ActionResult является то, что вы можете создать свой собственный вариант, который будет формировать результат в том виде, который вам нужен. Например, вы можете разработать класс, наследующий ActionResult, который будет возвращать клиентам результаты запроса в виде XML-разметки. Классическим примером создания своего варианта ActionResult является реализация класса, который на запрос пользователя создает ответ в виде RSS-ленты. Продемонстрируем реализацию такого класса, добавив к нашему контроллеру AdminController действие Rss, которое будет возвращать пользователю RSS-ленту со списком зарегистрированных пользователей.
Первым делом создадим класс RssResult, который наследует ActionResult, как показано в листинге 4.6.
Листинг 4.6. Класс RssResult
namespace MVCBookProject {
using System.Web.Mvc; using System.Xml;
using System.ServiceModel.Syndication;
public class RssResult : ActionResult {
public SyndicationFeed Feed { get; set; }
public RssResult(SyndicationFeed feed)
{
Feed = feed;
}
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.ContentType =
"application/rss+xml";
Rss20FeedFormatter formatter = new Rss20FeedFormatter(Feed);
using (XmlWriter writer =
XmlWriter.Create(context.HttpContext.Response.Output))
{
if (writer != null)
formatter.WriteTo(writer);
}
}
}
}
Обратите внимание, для реализации своего варианта ActionResult в классе RssResult мы перегружаем метод ExecuteResult, который и выполняет всю необходимую логику по формированию того результата, который получит клиент в своем браузере в ответ на запрос.
Использование класса RssResult ничем не отличается от применения других вариантов классов ActionResult. Добавим действие Rss в контроллер AdminControiier так, как показано во фрагменте:
[AcceptVerbs(HttpVerbs.Get)]
public RssResult Rss()
{
MembershipProvider mp = Membership.Provider;
int userCount;
var users = mp.GetAllUsers(0, Int32.MaxValue, out userCount);
List
if (userCount > 0)
{
string bodyTemplate = @"email: {0}, comment: {1},
last activity: {2}, is locked: {3}, is approved: {4}";
foreach (MembershipUser item in users)
{
string body = String.Format(bodyTemplate, item.Email,
item. Comment, item. LastActivityDate,
item.IsLockedOut, item.IsApproved);
items.Add(new SyndicationItem(item.UserName, body, null));
}
}
SyndicationFeed feed = new SyndicationFeed("Cписок пользователей",
"http://localhost/rss", Request.Url, items);
return new RssResult(feed);
}
Обратите внимание, что это действие возвращает результат в виде экземпляра класса RssResult, которому передается сгенерированный RSS-поток. После того как мы реализовали RssResult и действие Rss, можно попытаться запросить результат этого действия через браузер, перейдя по относительной ссылке /Admin/Rss. В итоге вы должны получить результат в виде RSS-потока, похожий на тот, который изображен на рис. 4.11.
Создание своих вариантов ActionResult – это исключительно мощное средство для расширения базовой функциональности MVC Framework. Реализуя свои экземпляры ActionResult, вы сможете генерировать ответ на клиентский запрос в любой форме с любой структурой данных.

Model Binding
После того как механизм MVC Framework получил запрос от клиента с набором некоторых параметров, произвел определение необходимого контроллера и действия, возникает задача сопоставления параметров запроса параметрам выбранного действия контроллера. Задача решается просто, когда параметров немного. В этом случае сопоставление параметров происходит по их наименованию: параметрам метода действия с определенным именем присваиваются значения параметров запроса с теми же именами. В качестве примера рассмотрим действие Update контроллера AdminController. Во фрагменте приведено определение метода с параметрами:
public ActionResult Update(Guid? userid, string email,
string comment, bool isApproved, bool isLockedOut)
Для этого действия подразумевается, что при его вызове будут переданы параметры с именами: userid, email, comment, isApproved, isLockedOut. Такие параметры передаются с запросом при отправлении формы с нашего представления Select. В следующем фрагменте рассмотрим основной HTML-код формы этого представления, который отображается в браузере пользователя:
После того как пользователь нажмет кнопку Сохранить, данные с формы отправляются на сервер в виде параметров с именами, определенными в разметке атрибутами name. После этого задача сопоставления параметров становится тривиальной.
Но что делать, когда форма содержит десятки вводимых полей? Неужели создавать десятки параметров у метода действия контроллера? Нет, MVC Framework содержит механизм, который позволяет избежать такого некрасивого шага, как многочисленные параметры метода. Такой механизм называется Model Binding (привязка модели). Чтобы продемонстрировать работу этого механизма, выполним ряд изменений в коде. Для начала определим комплексный тип, который будет содержать все необходимые данные, передаваемые в действие Update:
public class UserData
{
public Guid? UserId { get; set; }
public string Email { get; set; }
public string Comment { get; set; }
public bool IsApproved { get; set; }
public bool IsLockedOut { get; set; }
}
Обратите внимание, что для определения параметров мы используем свойства. Механизм Model Binding требует, чтобы использовались свойства, но не простые поля. Соответственно данному типу изменим определение метода Update:
public ActionResult Update(UserData userData)
{
if (!userData.UserId.HasValue)
throw new HttpException(404, "Пользователь не найден");
MembershipProvider mp = Membership.Provider;
MembershipUser user = mp.GetUser(userData.UserId, false);
user.Email = userData.Email;
user.Comment = userData.Comment;
user.IsApproved = userData.IsApproved;
if (user.IsLockedOut && !userData.IsLockedOut)
user.UnlockUser();
mp.UpdateUser(user);
return RedirectToAction("Index");
}
Теперь, чтобы механизм MVC Framework смог произвести сопоставление параметров с помощью встроенного механизма Model Binding, нам необходимо модифицировать код представления Select так, как показано в следующем фрагменте:
<% using (Html.BeginForm(«Update», «Admin»)) { %>
<%= Html.Hidden("userData.UserId", (Guid)user.ProviderUserKey)%>
<%= Html.AntiForgeryToken() %>
<% } %>
Обратите внимание на то, что для всех полей формы мы использовали наименование вида userData.Свойство. Например, поле email стало полем с именем userData.Email. Такое именование позволяет классу DefaultModelBinder, механизму Model Binding по умолчанию, сопоставить множественные параметры формы комплексному типу UserData.
Примечание
Строго говоря, в данном случае вам необязательно указывать для элементов формы префикс userData. Так как в сопоставлении участвует только один параметр комплексного типа, то механизм DefaultModelBinder автоматически определит значения его свойств, предположив, что все элементы формы относятся к единственному параметру userData. Однако мы рекомендуем указывать подобный префикс в любом случае для повышения читаемости кода и возможности более простого расширения кода в будущем.
*********************************************
Важной частью MVC Framework является возможность определять собственные механизмы Model Binding. Эта возможность предоставляет разработчику определять то, как параметры запроса или значения формы поступают к действию контроллера для обработки. Для демонстрации работы этого механизма добавим к нашей модели UserData еще одно свойство CurrentMembershipUser, которое будет автоматически инициализироваться при сопоставлении параметров:
public class UserData {
public MembershipUser CurrentMembershipUser { get; set; }
}
Теперь реализуем наш собственный механизм Model Binding, создав класс UserDataBinder, реализующий интерфейс IModelBinder. Этот интерфейс содержит всего один метод BindModel, с помощью которого и выполняется вся работа по сопоставлению параметров:
public class UserDataBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
UserData userData = new UserData();
userData.UserId = new
Guid(controllerContext.HttpContext.Request["UserId"]);
userData.Email = controllerContext.HttpContext.Request["Email"];
userData.Comment =
controllerContext.HttpContext.Request["Comment"];
userData.IsApproved =
controllerContext.HttpContext.Request["IsApproved"] != "false";
userData.IsLockedOut =
controllerContext.HttpContext.Request["IsLockedOut"] != "false";
MembershipProvider mp = Membership.Provider;
userData.CurrentMembershipUser =
mp.GetUser(userData.UserId, false);
return userData;
}
}
Обратите внимание на то, что при реализации своего механизма Model Binding мы сами указываем, какие параметры запроса и каким образом соответствуют ожидаемому комплексному типу вызываемого действия. Для того чтобы использовать эту реализацию интерфейса IModelBinder, мы должны зарегистрировать ее в Global.asax с помощью следующей конструкции:
protected void Application_Start()
{
...
ModelBinders.Binders.Add(typeof(UserData), new UserDataBinder());
}
Здесь мы добавляем в коллекцию еще один вариант Model Binder, который призван выполнять сопоставление типа UserData для всех действий любого контроллера в приложении.
Другим вариантом подключения нашего класса UserDataBinder может стать использование атрибута ModelBinderAttribute, в этом случае мы сможем явно указать, для какого конкретного параметра нужно использовать свой вариант Model Binder. ModelBinderAttribute позволяет более гибко управлять тем, когда и как применяются пользовательские элементы Model Binder, что не редко может быть полезным. При этом регистрировать в Global.asax UserDataBinder не потребуется. Используется атрибут ModelBinderAttribute следующим способом:
public ActionResult Update(
[ModelBinder(typeof(UserDataBinder))] UserData userData)
Как вы видите, в данном случае атрибут использован для конкретного параметра одного-единственного действия.
В общем случае использование стандартного варианта Model Binding в виде класса DefaultModelBinder достаточно для осуществления сопоставления параметров запроса и параметров метода действия. Однако существует еще одна полезная функция механизма Model Binding в MVC Framework. Эта функция реализуется атрибутом BindAttribute и позволяет еще более гибко настраивать процесс сопоставления параметров по умолчанию. Атрибут BindAttribute имеет следующие параметры:
□ Prefix – позволяет переопределить префикс при сопоставлении по умолчанию;
□ Include – позволяет определить список допустимых параметров, которые будут участвовать в сопоставлении, остальные параметры, не входящие в этот список, рассматриваться не будут;
□ Exclude – позволяет определить "черный" список параметров, которые не должны участвовать в процессе сопоставления. Такие параметры будут игнорироваться.
Использование параметра Prefix позволяет применять в представлении префикс для элементов формы, отличный от имени параметра метода действия. Например, вместо префикса userData в рассмотренном ранее примере, мы могли бы использовать сокращенный префикс ud, определив все элементы управления формы в подобном виде:
<%= Html.Hidden(«ud.UserId», (Guid)user.ProviderUserKey)%>
Чтобы механизм Model Binder по умолчанию узнал про наш новый префикс, необходимо задать атрибут BindAttribute в требуемом месте при определении параметров метода действия:
public ActionResult Update([Bind(Prefix = «ud»)] UserData userData)
Параметры Include и Exclude атрибута BindAttribute могут быть полезны в тех случаях, когда необходимо избежать автоматического сопоставления в комплексном типе для каких-то определенных свойств. Это может потребоваться для обеспечения безопасности или по каким-то другим соображениям. Например, чтобы запретить сопоставление свойства IsLockedOut, мы можем указать атрибут BindAttribute следующим образом:
public ActionResult Update(
[Bind(Exclude = "IsLockedOut")] UserData userData)
Иногда требуется задать определенный список разрешенных для сопоставления параметров. Для этого используется параметр Include, которому можно задать список разрешенных для сопоставления свойств. В следующем примере мы разрешаем для сопоставления только два свойства: Userid и Email:
public ActionResult Update(
[Bind(Include = "UserId, Email")] UserData userData)
Механизм сопоставления комплексных параметров форм с параметрами методов действий в MVC Framework значительно упрощается с помощью встроенного средства DefaultModelBinder. Этот механизм может гибко настраиваться с помощью атрибута BindAttribute, который позволяет задавать списки допустимых и недопустимых для сопоставления свойств и, вдобавок к этому, переопределять префикс, используемый в представлении. Если же разработчику недостаточно функционала механизма Model Binding по умолчанию, он волен переопределить этот механизм своей реализацией и использовать его как глобально во всем приложении, так и определяя его для конкретного параметра определенного действия.
Советы по использованию контроллеров
Атрибуты ActionNameSelectorAttribute и ActionNameAttribute
В механизме MVC существует множество полезных функций. Одна из них – это атрибут ActionNameAttribute, являющийся реализацией атрибута ActionNameSelectorAttribute – механизма MVC Framework, который позволяет ограничить выбор методов класса контроллера при определении нужного.
Атрибут ActionNameSelectorAttribute содержит всего один метод IsValidName со следующим определением:
public abstract bool IsValidName(ControllerContext controllerContext,
string actionName, MethodInfo methodInfo);
При поиске необходимого для выполнения действия механизм MVC Framework, кроме всего прочего, проверит все действия на наличие атрибута, реализующего ActionNameSelectorAttribute. В случае, когда такой атрибут найден, у него вызывается метод IsValidName для проверки на соответствие действия требуемому имени.
Единственная реализация ActionNameSelectorAttribute, существующая в MVC Framework, – это атрибут ActionNameAttribute, который призван предоставить возможность создания псевдонимов для методов действий. Рассмотрим следующий фрагмент кода:
[ActionName(«UserList»)]
public ActionResult GetUserListFromCache()
Здесь методу GetuserListFromCache, который представляет собой действие контроллера, присваивается укороченный псевдоним userList. После этого в ответ на запрос действия UserList контроллером может быть вызван метод GetuserListFromCache.
Наследование контроллеров
При работе с контроллерами в MVC Framework полезной практикой является механизм наследования контроллеров. Так как контроллеры представляют собой классы, преимущества наследования контроллеров схожи с преимуществами наследования классов в C#. Основным таким преимуществом является возможность создания базового набора правил для некоторого количества контроллеров. Для этого в MVC Framework можно определить контроллер, который будет называться базовым, и наследовать все остальные контроллеры от него.
Далее перечислены примеры возможных функций базовых контроллеров:
□ хранение и предоставление информации о текущем пользователе и его правах;
□ предоставление информации о базовых настройках приложения, которые могут поставляться, например, из web.config;
□ экземпляры хранилищ кода для работы с разнообразным функционалом: от пользователей до специфических данных приложения;
□ вспомогательные статические или другие методы утилитарного характера;
□ определение набора атрибутов, которые будут наследовать потомки базового контроллера.
Рассмотрим простейший пример базового контроллера. Для этого определим для него набор функционала: обработку ошибок, GZip-сжатие результатов действий, вспомогательную функцию для работы с пользователем и загрузчик некоторых параметров из файла web.config.
Листинг 4.7. Базовый контроллер
[HandleError(View = "AdminError")]
[GZipCompress]
public class BaseController : Controller
{
public NameValueCollection Settings
{
get
{
return ConfigurationManager.AppSettings;
}
}
public string UserNotFoundMessage
{
get
{
return Settings["userNotFoundMessage"];
}
}
public readonly MembershipProvider MP = Membership.Provider;
public virtual ActionResult Index()
{
return View();
}
public static MembershipUser GetUser(string userName)
{
MembershipProvider mp = Membership.Provider;
return mp.GetUser(userName, false);
}
public static MembershipUser GetUser(Guid userId)
{
MembershipProvider mp = Membership.Provider;
return mp.GetUser(userId, false);
}
}
Базовый контроллер из листинга 4.7 обладает следующими свойствами:
□ определяет для контроллера действие по умолчанию Index;
□ определяет атрибуты по умолчанию для обработки ошибок и сжатия результатов через GZip;
□ определяет обертку Settings над секцией настроек appSettings файла web.config, для более прозрачного доступа к настройкам;
□ прямо определяет UserNotFoundMessage, одну из настроек секции appSettings для быстрого к ней доступа;
□ определяет упрощенный доступ к объекту Membership.Provider;
□ определяет статический метод для более простого доступа к данным пользователей.
Чтобы наделить этими свойствами любой контроллер, необходимо наследовать его от базового. Модифицируем контроллер AdminController согласно новым правилам так, как показано во фрагменте:
public class AdminController : BaseController {
[AcceptVerbs(HttpVerbs.Get)]
[Authorize(Users = "Admin")]
public override ActionResult Index()
{
int userCount;
var users = MP.GetAllUsers(0, Int32.MaxValue, out userCount);
ViewData.Model = users;
return View();
}
[AcceptVerbs(HttpVerbs.Get)]
[Authorize(Users = "Admin")]
public ActionResult Select(Guid? userId)
{
if (!userId.HasValue)
throw new HttpException(404, UserNotFoundMessage);
return View(«Select», «Site», GetUser(userId.Value));
}
}
Обратите внимание, класс контроллера наследует BaseController, в связи с этим действие Index переопределяется с помощью ключевого слова override. Кроме того, в Index используется новое свойство mp, определенное в базовом контроллере. В другом действии, Select, используются два других функционала базового контроллера: свойство UserNotFoundMessage и статический метод GetUser.
Использование базовых контроллеров позволяет гибко определять базовую логику для других контроллеров. Создав однажды базовый контроллер с набором функций, впоследствии, при создании других контроллеров, вы можете наследовать эти функции, просто определяя базовый контроллер для каждого нового контроллера.
Асинхронное выполнение при работе с контроллерами
При создании веб-приложений часто может возникнуть проблема с обработкой данных, которая отнимает большие ресурсы и машинное время. Механизм ASP.NET имеет ограниченное количество потоков, которые предназначены для обработки пользовательских запросов, полученных от сервера IIS. Проблема состоит в том, что если один из запросов предполагает продолжительную работу с привлечением больших ресурсов, то такой запрос может уменьшить пропускную способность сайта. В случаях, когда таких запросов много, их выполнение может вообще заблокировать доступ пользователей к ресурсу, т. к. все рабочие потоки ASP.NET будут заняты, простаивая в ожидании того, когда завершится выполнение тяжелого запроса к базе данных или сложное вычисление.
Выходом из такой ситуации может служить асинхронное выполнение запросов. При асинхронном выполнении тяжелая задача поручается для выполнения отдельному специально созданному потоку, а основной поток ASP.NET освобождается для обработки других пользовательских запросов. Для реализации такого функционала разработчиками MVC Framework был создан специальный механизм AsyncController, который хоть и не вошел в MVC Framework, но доступен в особой библиотеке MVC Framework Futures, которая представлена файлом Microsoft.Web.Mvc.dll.
Примечание
Саму библиотеку и документацию к ней на английском языке можно скачать с официальной страницы ASP.NET MVC на сайте Codeplex по следующему адресу: http://aspnet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=24471
******************************
После добавления ссылки на сборку в проект, для того чтобы использовать асинхронные контроллеры, необходимо проделать некоторые изменения в существующем коде. Первым делом нужно изменить регистрации маршрутов в таблице маршрутизации так, как показано в следующем фрагменте:
routes.MapAsyncRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);
Обратите внимание на то, что вызов routes.MapRoute заменен на routes.MapAsyncRoute, это необходимо, чтобы механизм MVC мог обрабатывать как асинхронные, так и синхронные контроллеры. После изменений в регистрации маршрутов нет нужды в других изменениях, чтобы специально отслеживать синхронные контроллеры, поскольку механизм MapAsyncRoute регистрирует маршруты как для асинхронных, так и синхронных контроллеров.
После изменения регистрации маршрутов следует изменить обработчики для *.mvc, определенные ранее в web.config, следующим образом в разделах httpHandlers и handlers:
type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> verb="*" path="*.mvc" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> Эти определения обработчиков необходимо заменить на следующие: type="Microsoft.Web.Mvc.MvcHttpAsyncHandler, Microsoft.Web.Mvc"/> verb="*" path="*.mvc" type="Microsoft.Web.Mvc.MvcHttpAsyncHandler, Microsoft.Web.Mvc"/> После всех изменений можно приступать к работе с асинхронными контроллерами. Для того чтобы класс контроллера стал асинхронным, необходимо наследовать его от класса AsyncController: public class SomeAsyncController : AsyncController { ... } После этого конструктор по умолчанию, унаследованный от AsyncController, определит новый вариант ActionInvoker в виде экземпляра класса AsyncControllerActionInvoker для того, чтобы выполнять асинхронные действия. Для реализации асинхронных действий механизм AsyncController предлагает три паттерна, которые вы вольны использовать по отдельности либо смешивать их друг с другом: IAsyncResult, Event, Delegate. Паттерн IAsyncResult Паттерн IAsyncResult предполагает, что разработчик сам создаст асинхронную операцию. Согласно этому паттерну, вместо одного метода действия с именем XXX создаются два метода, BeginXXX и EndXXX, со следующим определением параметров: public IAsyncResult BeginXXX(Guid? userId, AsyncCallback callback, object state); public ActionResult EndXXX(IAsyncResult asyncResult); Этот паттерн работает следующим образом: 1. MVC принимает запрос на выполнение действия xxx. 2. Механизмы MVC и AsyncController вызовут BeginXXX точно так же, как и любое другое синхронное действие. 3. Предполагается, что метод Beginxxx создаст некую тяжеловесную асинхронную операцию, например файловое чтение или запрос к базе данных, и использует переданную через параметры функцию обратного вызова callback для вызова после завершения асинхронной операции. 4. После выполнения асинхронной операции будет вызван второй метод Endxxx, которому будет передан результат выполнения Beginxxx в виде экземпляра IAsyncResult. 5. Метод Beginxxx, используя данные, полученные от Beginxxx, формирует обычный для всех действий результат в виде ActionResult или его производных. Паттерн Event Согласно этому паттерну, метод действия разделяется на два метода: запуска и завершения: public void XXX(Guid? userId); public ActionResult XXXCompleted(...); Метод xxx соответствует обычному синхронному действию и вызывается стандартно. Полный механизм работы данного паттерна состоит из следующих действий: 1. MVC принимает запрос на выполнение действия xxx. 2. Механизмы MVC и AsyncController вызовут XXX точно так же, как и любое другое синхронное действие. 3. Разработчик определяет внутри метода xxx асинхронную операцию, после запуска которой метод завершает свое выполнение. 4. Чтобы механизм асинхронных контроллеров мог определить, когда следует вызвать XXXCompleted, разработчик должен воспользоваться свойством AsyncManager.OutstandingOperations, которое является стандартным для класса контроллера AsyncController. 5. Разработчик инкрементирует AsyncManager.OutstandingOperations при создании каждого асинхронного процесса и заботится о том, чтобы по завершению процесса свойство AsyncManager.OutstandingOperations было декрементировано. 6. Механизм AcyncControllerActionInvoker следит за свойством AsyncManager.OutstandingOperations и вызывает XXXCompleted, когда это свойство обнулится, что означает завершение работы всех асинхронных процессов. 7. Параметры для XXXCompleted определяет разработчик. Для того чтобы AcyncControllerActionInvoker мог правильно выполнить XXXCompleted и передать необходимые параметры, разработчик заполняет специальную структуру AsyncManager.Parameters, которая является частью класса AsyncController. Структура AsyncManager.Parameters заполняется при работе метода xxx. 8. После завершения работы XXX механизм AcyncControllerActionInvoker вызывает метод XXXCompleted с набором параметров на базе AsyncManager.Parameters.




























