I'm currently trying to create an IHttpActionInvoker
for use with ASP.NET Web API that will allow a result to be an Async<'T>
. At the moment, I'm ignoring conversion of IHttpActionResult
s and only care about HttpResponseMessage
and a value of type 'T
. I currently have the following implementation:
我目前正在尝试创建一个用于ASP的IHttpActionInvoker。允许结果为异步<'T>的NET Web API。目前,我忽略了IHttpActionResults的转换,只关心HttpResponseMessage和类型为t的值。
type AsyncApiActionInvoker() =
inherit Controllers.ApiControllerActionInvoker()
override x.InvokeActionAsync(actionContext, cancellationToken) =
if actionContext = null then
raise <| ArgumentNullException("actionContext")
let actionDescriptor = actionContext.ActionDescriptor
Contract.Assert(actionDescriptor <> null)
if actionDescriptor.ReturnType = typeof<Async<HttpResponseMessage>> then
let controllerContext = actionContext.ControllerContext
Contract.Assert(controllerContext <> null)
let task = async {
let! asyncResult = Async.AwaitTask <| actionDescriptor.ExecuteAsync(controllerContext, actionContext.ActionArguments, cancellationToken)
// For now, throw if the result is an IHttpActionResult.
if typeof<IHttpActionResult>.IsAssignableFrom(actionDescriptor.ReturnType) then
raise <| InvalidOperationException("IHttpResult is not supported when returning an Async")
let! result = asyncResult :?> Async<HttpResponseMessage>
return actionDescriptor.ResultConverter.Convert(controllerContext, result) }
Async.StartAsTask(task, cancellationToken = cancellationToken)
else base.InvokeActionAsync(actionContext, cancellationToken)
This works for Async<HttpResponseMessage>
only. If I try to cast to Async<_>
, I get an exception stating that I can't cast to Async<obj>
. I also cannot correctly detect whether or not the actionDescriptor.ReturnType
is an Async<_>
. This does not surprise me, but I'm not sure how to get around the problem.
这只适用于异步
2 个解决方案
#1
2
as an option (browser-compiled code, may contain errors)
作为选项(浏览器编译的代码可能包含错误)
let (|Async|_|) (ty: Type) =
if ty.IsGenericType && ty.GetGenericTypeDefinition() = typedefof<Async<_>> then
Some (ty.GetGenericArguments().[0])
else
None
type AsyncApiActionInvoker() =
inherit Controllers.ApiControllerActionInvoker()
static let AsTaskMethod = typeof<AsyncApiActionInvoker>.GetMethod("AsTask")
static member AsTask<'T> (actionContext: Controllers.HttpActionContext, cancellationToken: CancellationToken) =
let action = async {
let task =
actionContext.ActionDescriptor.ExecuteAsync(
actionContext.ControllerContext,
actionContext.ActionArguments,
cancellationToken
)
let! result = Async.AwaitTask task
let! asyncResult = result :?> Async<'T>
return actionContext.ActionDescriptor.ResultConverter.Convert(actionContext.ControllerContext, box asyncResult)
}
Async.StartAsTask(action, cancellationToken = cancellationToken)
override x.InvokeActionAsync(actionContext, cancellationToken) =
if actionContext = null then
raise <| ArgumentNullException("actionContext")
match actionContext.ActionDescriptor.ReturnType with
| Async resultType ->
let specialized = AsTaskMethod.MakeGenericMethod(resultType)
downcast specialized.Invoke(null, [|actionContext, cancellationToken|])
| _ -> base.InvokeActionAsync(actionContext, cancellationToken)
#2
0
After several helpful tips from outside *, I came up with the following solution that appears to work. I'm not thrilled with it, but it does the job. I'd appreciate any tips or pointers:
在*外提供了一些有用的提示之后,我找到了以下似乎有效的解决方案。我对它不感兴趣,但它能胜任这份工作。如有任何建议或建议,我将不胜感激。
type AsyncApiActionInvoker() =
inherit Controllers.ApiControllerActionInvoker()
static member internal GetResultConverter(instanceType: Type, actionDescriptor: HttpActionDescriptor) : IActionResultConverter =
if instanceType <> null && instanceType.IsGenericParameter then
raise <| InvalidOperationException()
if instanceType = null || typeof<HttpResponseMessage>.IsAssignableFrom instanceType then
actionDescriptor.ResultConverter
else
let valueConverterType = typedefof<ValueResultConverter<_>>.MakeGenericType instanceType
let newInstanceExpression = Expression.New valueConverterType
let ctor = Expression.Lambda<Func<IActionResultConverter>>(newInstanceExpression).Compile()
ctor.Invoke()
static member internal StartAsTask<'T>(task, resultConverter: IActionResultConverter, controllerContext, cancellationToken) =
let computation = async {
let! comp = Async.AwaitTask task
let! (value: 'T) = unbox comp
return resultConverter.Convert(controllerContext, value) }
Async.StartAsTask(computation, cancellationToken = cancellationToken)
override this.InvokeActionAsync(actionContext, cancellationToken) =
if actionContext = null then
raise <| ArgumentNullException("actionContext")
let actionDescriptor = actionContext.ActionDescriptor
Contract.Assert(actionDescriptor <> null)
let returnType = actionDescriptor.ReturnType
// For now, throw if the result is an IHttpActionResult.
if typeof<IHttpActionResult>.IsAssignableFrom(returnType) then
raise <| InvalidOperationException("IHttpResult is not supported when returning an Async")
if returnType.IsGenericType && returnType.GetGenericTypeDefinition() = typedefof<Async<_>> then
let controllerContext = actionContext.ControllerContext
Contract.Assert(controllerContext <> null)
let computation = actionDescriptor.ExecuteAsync(controllerContext, actionContext.ActionArguments, cancellationToken)
let innerReturnType = returnType.GetGenericArguments().[0]
let converter = AsyncApiActionInvoker.GetResultConverter(innerReturnType, actionDescriptor)
this.GetType()
.GetMethod("StartAsTask", BindingFlags.NonPublic ||| BindingFlags.Static)
.MakeGenericMethod(innerReturnType)
.Invoke(null, [| computation; converter; controllerContext; cancellationToken |])
|> unbox
else base.InvokeActionAsync(actionContext, cancellationToken)
I hope this helps someone else!
我希望这能帮助别人!
#1
2
as an option (browser-compiled code, may contain errors)
作为选项(浏览器编译的代码可能包含错误)
let (|Async|_|) (ty: Type) =
if ty.IsGenericType && ty.GetGenericTypeDefinition() = typedefof<Async<_>> then
Some (ty.GetGenericArguments().[0])
else
None
type AsyncApiActionInvoker() =
inherit Controllers.ApiControllerActionInvoker()
static let AsTaskMethod = typeof<AsyncApiActionInvoker>.GetMethod("AsTask")
static member AsTask<'T> (actionContext: Controllers.HttpActionContext, cancellationToken: CancellationToken) =
let action = async {
let task =
actionContext.ActionDescriptor.ExecuteAsync(
actionContext.ControllerContext,
actionContext.ActionArguments,
cancellationToken
)
let! result = Async.AwaitTask task
let! asyncResult = result :?> Async<'T>
return actionContext.ActionDescriptor.ResultConverter.Convert(actionContext.ControllerContext, box asyncResult)
}
Async.StartAsTask(action, cancellationToken = cancellationToken)
override x.InvokeActionAsync(actionContext, cancellationToken) =
if actionContext = null then
raise <| ArgumentNullException("actionContext")
match actionContext.ActionDescriptor.ReturnType with
| Async resultType ->
let specialized = AsTaskMethod.MakeGenericMethod(resultType)
downcast specialized.Invoke(null, [|actionContext, cancellationToken|])
| _ -> base.InvokeActionAsync(actionContext, cancellationToken)
#2
0
After several helpful tips from outside *, I came up with the following solution that appears to work. I'm not thrilled with it, but it does the job. I'd appreciate any tips or pointers:
在*外提供了一些有用的提示之后,我找到了以下似乎有效的解决方案。我对它不感兴趣,但它能胜任这份工作。如有任何建议或建议,我将不胜感激。
type AsyncApiActionInvoker() =
inherit Controllers.ApiControllerActionInvoker()
static member internal GetResultConverter(instanceType: Type, actionDescriptor: HttpActionDescriptor) : IActionResultConverter =
if instanceType <> null && instanceType.IsGenericParameter then
raise <| InvalidOperationException()
if instanceType = null || typeof<HttpResponseMessage>.IsAssignableFrom instanceType then
actionDescriptor.ResultConverter
else
let valueConverterType = typedefof<ValueResultConverter<_>>.MakeGenericType instanceType
let newInstanceExpression = Expression.New valueConverterType
let ctor = Expression.Lambda<Func<IActionResultConverter>>(newInstanceExpression).Compile()
ctor.Invoke()
static member internal StartAsTask<'T>(task, resultConverter: IActionResultConverter, controllerContext, cancellationToken) =
let computation = async {
let! comp = Async.AwaitTask task
let! (value: 'T) = unbox comp
return resultConverter.Convert(controllerContext, value) }
Async.StartAsTask(computation, cancellationToken = cancellationToken)
override this.InvokeActionAsync(actionContext, cancellationToken) =
if actionContext = null then
raise <| ArgumentNullException("actionContext")
let actionDescriptor = actionContext.ActionDescriptor
Contract.Assert(actionDescriptor <> null)
let returnType = actionDescriptor.ReturnType
// For now, throw if the result is an IHttpActionResult.
if typeof<IHttpActionResult>.IsAssignableFrom(returnType) then
raise <| InvalidOperationException("IHttpResult is not supported when returning an Async")
if returnType.IsGenericType && returnType.GetGenericTypeDefinition() = typedefof<Async<_>> then
let controllerContext = actionContext.ControllerContext
Contract.Assert(controllerContext <> null)
let computation = actionDescriptor.ExecuteAsync(controllerContext, actionContext.ActionArguments, cancellationToken)
let innerReturnType = returnType.GetGenericArguments().[0]
let converter = AsyncApiActionInvoker.GetResultConverter(innerReturnType, actionDescriptor)
this.GetType()
.GetMethod("StartAsTask", BindingFlags.NonPublic ||| BindingFlags.Static)
.MakeGenericMethod(innerReturnType)
.Invoke(null, [| computation; converter; controllerContext; cancellationToken |])
|> unbox
else base.InvokeActionAsync(actionContext, cancellationToken)
I hope this helps someone else!
我希望这能帮助别人!