На самом деле это похоже на программиста, который сделал Морозов. Он сделал обычай AsyncWorkflowController
, который позволяет вернуть Async<ActionResult>
от ActionResult
. Код для AsyncWorkFlowController
можно найти по адресу http://fssnip.net/5q.
Однако его реализация очень затрудняет отладку из-за того, что трассировка стека не будет сохранена при повторном запуске в пользовательском контроллере. Поэтому я сделал небольшое изменение, чтобы сделать это возможным:
member actionDesc.EndExecute(asyncResult) =
match endAsync'.Value(asyncResult) with
| Choice1Of2 value -> box value
| Choice2Of2 why ->
// Preserve the stack trace, when rethrow
ExceptionDispatchInfo.Capture(why).Throw()
obj() (* Satisfy return value *) } } }
Также я изменил следующую строку: new ReflectedControllerDescriptor(controllerType)
,
к new ReflectedAsyncControllerDescriptor(controllerType)
- Однако это изменение чисто необязательно, так как он не будет делать какие-либо разница. Я просто счел более логичным использовать Async
.
Полный код будет таким:
open System
open System.Web.Mvc
open System.Web.Mvc.Async
open System.Runtime.ExceptionServices
open Unchecked
type AsyncWorkflowController() =
inherit AsyncController()
override __.CreateActionInvoker() =
upcast { new AsyncControllerActionInvoker() with
member __.GetControllerDescriptor(controllerContext) =
let controllerType = controllerContext.Controller.GetType()
upcast { new ReflectedAsyncControllerDescriptor(controllerType) with
member ctrlDesc.FindAction(controllerContext, actionName) =
let forwarder = base.FindAction(controllerContext, actionName) :?> ReflectedActionDescriptor
if(forwarder = null || forwarder.MethodInfo.ReturnType <> typeof<Async<ActionResult>>) then
upcast forwarder
else
let endAsync' = ref (defaultof<IAsyncResult -> Choice<ActionResult, exn>>)
upcast { new AsyncActionDescriptor() with
member actionDesc.ActionName = forwarder.ActionName
member actionDesc.ControllerDescriptor = upcast ctrlDesc
member actionDesc.GetParameters() = forwarder.GetParameters()
member actionDesc.BeginExecute(controllerContext, parameters, callback, state) =
let asyncWorkflow =
forwarder.Execute(controllerContext, parameters) :?> Async<ActionResult>
|> Async.Catch
let beginAsync, endAsync, _ = Async.AsBeginEnd(fun() -> asyncWorkflow)
endAsync' := endAsync
beginAsync((), callback, state)
member actionDesc.EndExecute(asyncResult) =
match endAsync'.Value(asyncResult) with
| Choice1Of2 value -> box value
| Choice2Of2 why ->
// Preserve the stack trace, when rethrow
ExceptionDispatchInfo.Capture(why).Throw()
obj() (* Satisfy return value *) } } }
Использование:
type TestController() =
inherit AsyncWorkflowController()
member x.IndexWorks() = async {
let startThread = Thread.CurrentThread.ManagedThreadId
let! data = asyncDownload "http://stackoverflow.com"
let endThread = Thread.CurrentThread.ManagaedThreadId
return ContentResult(Content = "Start = %i | End = %i" startThread endThread) :> ActionResult }
И чтобы подтвердить, что он на самом деле делает все асинхронной и не блокирует любые темы из ASP.NET Pool, использование:
member x.IndexWorks() = async {
let startThread = Thread.CurrentThread.ManagedThreadId
let! data = asyncDownload "http://stackoverflow.com"
let endThread = Thread.CurrentThread.ManagaedThreadId
return ContentResult(Content = "Start = %i | End = %i" startThread endThread) :> ActionResult }
Начальная и конечная нить будут отличаться, поэтому начальная нить была помещена обратно в poo l, а новое было возвращено, когда операция async завершилась.
по первому варианту, говорится, что он не может получить доступ к защищенным членам из лямбда – Maslow