Шрифт:
8. После завершения работы XXX механизм AcyncControllerActionInvoker вызывает метод XXXCompleted с набором параметров на базе AsyncManager.Parameters.
9. Используя переданные параметры, метод XXXCompleted возвращает стандартный результат в виде ActionResult или его производных.
Следующий фрагмент кода демонстрирует реализацию паттерна Event:
public void SelectUser(Guid? userId)
{
AsyncManager.Parameters["userData''] = new UserData;
AsyncManager.OutstandingOperations.Increment;
ThreadPool.QueueUserWorkItem(o =>
{
Thread.Sleep(2000);
AsyncManager.OutstandingOperations.Decrement;
}, null);
}
public ActionResult SelectUserCompleted(UserData userData)
{
...
}
Чтобы упростить процесс, существует альтернатива прямому инкрементированию и декрементированию. С помощью стандартной части AsyncController метода AsyncManager.RegisterTask можно использовать связку из паттернов IAsyncResult и Event. Рассмотрим на примере:
public void XXX(Guid? userId)
{
AsyncManager.RegisterTask(
callback => BeginXXX(userId, callback, null),
asyncResult =>
{
UserData userData = EndXXX(asyncResult);
AsyncManager.Parameters["userData"] = userData;
}
);
}
public ActionResult XXXCompleted(UserData userData)
{
// ...
}
Во фрагменте кода используется паттерн Event, согласно которому создается два метода: xxx и xxxCompleted. Метод xxx регистрирует асинхронную задачу с помощью механизма AsyncManager.RegisterTask, который принимает два параметра: анонимные функции, осуществляющие логику паттерна IAsyncResult. Первая функция вызывает метод Beginxxx, который выполняет некую асинхронную операцию. Вторая анонимная функция выполняется тогда, когда асинхронная операция заканчивается и ей передаются результаты вызова Beginxxx. Задача второй анонимной функции состоит в том, чтобы, используя метод Endxxx паттерна IAsyncResult, получить значения параметров для метода XXXCompleted.
Преимущество данной связки паттернов состоит в том, что разработчику предоставляется возможность создавать несколько следующих подряд вызовов механизма AsyncManager.RegisterTask, которые выполняют разные асинхронные операции и формируют результаты разных параметров для метода XXXCompleted. Так как механизм AsyncManager.RegisterTask самостоятельно отслеживает инкремент и декремент свойства AsyncManager.OutstandingOperations, разработчику предлагается более безопасный и простой механизм создания нескольких асинхронных операций в одном запросе.
Паттерн Delegate
Этот паттерн похож на паттерн Event с одним существенным отличием: отсутствует метод xxxComplete. Вместо этого метод xxx сам занимается возвращением результата ActionResult на основании данных, полученных от асинхронных операций. Так выглядит определение метода действия при использовании паттерна Delegate:
public Func<ActionResult> Foo(Guid? userId)
Для демонстрации реализации данного паттерна перепишем пример паттерна Event по-другому:
public Func<ActionResult> XXX(Guid userId)
{
UserData userData = new UserData;
AsyncManager.RegisterTask(
callback => BeginXXX(userId, callback, null),
asyncResult =>
{
userData = EndXXX(asyncResult);
}
);
return => {
ViewData["userData"] = userData;
return View ;
};
}
Главное отличие реализации паттерна Delegate в приведенном фрагменте от паттерна Event состоит в том, что для возвращения результата выполнения действия используется не ActionResult, а Func<ActionResult>, который представляет собой анонимную функцию, возвращающую результат в виде ActionResult. По сравнению с паттерном Event данный паттерн имеет упрощенный единый механизм, не разделенный на несколько методов, и максимально напоминает работу действий в синхронных контроллерах. При использовании этого паттерна у разработчика нет необходимости заботиться ни об обработке AsyncManager.OutstandingOperations, ни о заполнении AsyncManager.Parameters.
Дополнительные сведения об асинхронных контроллерах
Для асинхронных операций важно понятие времени исполнения запроса, поэтому в стандартный механизм класса AsyncController входит свойство AsyncManager.Timeout, которое позволяет задавать максимальное время ожидания результата выполнения асинхронного действия. В случае, когда действие выполняется дольше, чем определено в AsyncManager.Timeout механизмом, будет вызвано исключение TimeoutException. Для более гибкого управления максимальным периодом ожидания ответа от действия механизм асинхронных контроллеров предлагает два атрибута: AsyncTimeoutAttribute и NoAsyncTimeoutAttribute. Первый устанавливает время ожидания для конкретного действия или контроллера, второй указывает, что ожидания ответа не должно вызвать исключения и ожидать ответа от асинхронного действия требуется без ограничения по времени.
Одним из ограничений механизма асинхронных контроллеров является ограничение на именование методов действий. Вы не можете называть методы действий с префиксами Begin, End и суффиксом Completed. Это ограничение призвано предотвратить прямой вызов методов типа BeginXXX, EndXXX или XXXCompleted вместо вызова XXX. Тем не менее вы можете воспользоваться атрибутом ActionNameAttribute для того, чтобы задать необходимый псевдоним методу действия. Следующий фрагмент демонстрирует это:
[ActionName("BeginProcess")]