Support F# async like the Task based async in actions.
So an action like the following can be used OOTB.
member this.About () = async {
let! msg = getDescription ()
this.ViewData.["Message"] <- msg
return this.View()
}
I added an example aspnet project with both F# async and current workaround actions.
Example is based on dotnet new -l fsharp -t web template in preview4, but is the same for all sdk versions (preview2 etc)
The f# async Home/About doesnt work (async class is converted as string => {}), converting as Task works in Home/About2
The workaround is to call Async.StartAsTask before returning the FSharpAsync, so is converted as Task.
Like xunit does, recognize the result is FSharpAsync and invoke Async.StartAsTask (ref xunit commit to add f# async support https://github.com/xunit/xunit/commit/a2aa665f1b9633e8ec00f549c5df73a0c784a742 )
In previous aspnet was possibile to use an ActionResult.
Speaking with @davidfowl he directed me into ObjectMethodExecutor class.
It should not give runtime performance penality (f# or c#), because it's done at initialization step also for normal Task, so at runtime just use a different delegate configured in ObjectMethodExecutor (in GetCoerceMethodCallExpression?)
/cc @panesofglass
If the approach is correct and approved as idea, can be put as up-for-grab? if noone want to step in, i'll fix that later.
/cc @Eilon thoughts? Having looked at the xunit approach, I like that they avoid adding a hard dependency on any F# libraries.
I think we should do this.
PS: Whatever we do here we'll want similar logic in Common because SignalR will be the next thing to implement this.
If it's ok to hardcode it (clean job and reusable obv) for now as suggested, it will be possible to refactor out and move as external library (initialized in Startup or when creating the WebHostBuilder) when an extension point exists (and add that library only to f# aspnet template). But is a small use case for an extension point, i cannot think of others (non f# too) atm because serializers already understand f# types. We are just starting using aspnet with f#, so let's gather feedback first 馃槃
For example for aspnet core handlers, we dont need changes, we just use an extension method to overload IApplicationBuilder.Run method
let myHandler (context: HttpContext) = async {
do! context.Response.WriteAsync("Hello World from F#!") |> Async.AwaitTask
}
app.Run(myHandler)
And extension will be inside an helper lib soon (need to publish that 馃槃 , but following code is ok too to embedd)
[<AutoOpen>]
module Async =
// Async<unit> -> Task
let inline StartAsPlainTask (work : Async<unit>) = work |> Async.StartAsTask :> Task
//Extension method used to Run an Async<unit> as RequestDelegate
type Microsoft.AspNetCore.Builder.IApplicationBuilder with
member this.Run(handler : HttpContext -> Async<unit>) =
this.Run(RequestDelegate(handler >> StartAsPlainTask))
I hope to resolve it once and for all for all libs, but no clue how to do it, so fix every lib when needed is the best current option.
@rynowak @Eilon @davidfowl sry for ping, just to not stop the discussion, because i'd like to add that if is approved as idea.
Would that mean that async getter-only properties are supported as well? Or does it have to be a method?
type MyController () =
inherit Controller ()
// Would this be possible?
member this.IndexProperty = async {
return this.View ()
}
// Or does it have to be a method?
member this.IndexMethod () = async {
return this.View ()
}
@nikonthethird - no plans to change what we consider an action as part of this work, only methods.
You could write something that adds support properties if you want to, but out of the box we'd only consider methods.
Hi @enricosada , sorry for the delay. We would certainly accept a PR for this if you have time to work for it.
See #6040 - while were here we should do valuetask as well
Done since https://github.com/aspnet/Common/pull/221 now understands FSharpAsync<T>.
Have added a functional test to show it works end-to-end: https://github.com/aspnet/Mvc/pull/6240
Most helpful comment
See #6040 - while were here we should do valuetask as well