我是F#的新手并试图将一些C#ASP.NET核心代码翻译成F#
即使我让它工作,我似乎无法弄清楚如何使控制器操作异步。这些方法在Commands对象和注入的Queries对象上调用异步代码。命令和查询目前在C#中实现。
例如,一些异步C#控制器方法是:
public async Task<IEnumerable<ToDoItem>> Get()
{
return await queries.GetAll();
}
[HttpGet("{id}", Name = "GetTodo")]
public async Task<IActionResult> GetById(string id)
{
var item = await queries.Find(id);
if (item == null)
{
return NotFound();
}
return new ObjectResult(item);
}
public async Task<IActionResult> Create([FromBody] ToDoItem item)
{
if (item == null)
{
return BadRequest();
}
if (string.IsNullOrEmpty(item.Id)) item.Id = Guid.NewGuid().ToString();
await commands.Add(item);
return CreatedAtRoute("GetTodo", new { id = item.Id }, item);
}
我已将这些翻译成F#:
[<HttpGet>]
member __.Get() =
__.Queries.GetAll() // this should be awaited
[<HttpGet("{id}", Name = "GetFSTodo")>]
member __.GetToDoItem(id) =
let data = __.Queries.Find(id) // this should be awaited
if isNull data
then __.NotFound() :> IActionResult
else
new ObjectResult(data) :> IActionResult
[<HttpPost>]
member __.Create([<FromBody>] item:ToDoItem) =
item.Id <- Guid.NewGuid().ToString()
(__.Commands.Add(item)) |> ignore // this should be awaited
let rv = new RouteValueDictionary()
rv.Add("id",item.Id)
__.CreatedAtRoute("GetTodo", rv, item) :> IActionResult
这些方法有效,但我认为它们没有正确完成,因为它们没有等待查询和命令的异步调用。我通过反复试验打了好几个小时但是我为使控制器方法异步而做的每一次尝试都导致他们没有将任何数据返回给浏览器,即使他们返回200状态代码。您可以在F# controller
中看到我的一些尝试希望一些F#guru可以帮助我正确翻译这些方法。目前在使用ASP.NET Core的F#方面存在一些非常糟糕的工具问题,这对像我这样的新手来说更加困难。我在readme
中提到了这些问题代码中还有一些其他方法,但我想如果我可以学习如何解决这些方法,那么相同的解决方案可能适用于其他方法。
代码位于公共存储库中,因此只要您安装了最新的VS更新和最新的ASP.NET Core工具,就可以在VS 2015中轻松尝试它。
更新:
感谢Mark Seemann的链接帖子,我能够让这个方法工作异步
[<HttpGet("{id}", Name = "GetFSTodo")>]
member __.GetToDoItem(id) =
async {
let! data = __.Queries.Find(id) |> asyncReturn
if isNull data
then return __.NotFound() :> IActionResult
else
return new ObjectResult(data) :> IActionResult }
|> Async.StartAsTask
使用辅助函数
let asyncReturn x = async { return x }
我仍在努力使用这种方法
[<HttpGet>]
member __.Get() =
async {
let! data = __.Queries.GetAll() |> asyncReturn
return data }
|> Async.StartAsTask
从这个C#方法翻译而来:
[HttpGet]
public async Task<IEnumerable<ToDoItem>> Get()
{
return await queries.GetAll();
}
异步F#方法有效,但它产生的json输出与C#版本不同
C#
[{"id":"4f4e1596-6a48-4854-9982-7a2568aa1b1b","title":"add input validation","isDone":false,"dateAdded":"2016-09-20T21:16:04.8791044Z"},{"id":"9929b657-6a53-40b6-8c1c-1e4d0db593cd","title":"make F# controller async","isDone":false,"dateAdded":"2016-09-21T19:36:44.6650776Z"},{"id":"5bb5f544-6289-4051-ad65-d0dc016128e7","title":"learn F# basics","isDone":true,"dateAdded":"2016-09-22T11:59:00"},{"id":"e5e06118-c49f-496a-8175-9719ea72beed","title":"monkey business","isDone":false,"dateAdded":"2016-09-22T16:22:20.3133161Z"},{"id":"af0db8f2-6b49-4e31-86fa-e27c8e091f42","title":"funky bidness","isDone":false,"dateAdded":"2016-09-22T16:23:35.1175195Z"}]
F#
{"result":[{"id":"4f4e1596-6a48-4854-9982-7a2568aa1b1b","title":"add input validation","isDone":false,"dateAdded":"2016-09-20T21:16:04.8791044Z"},{"id":"9929b657-6a53-40b6-8c1c-1e4d0db593cd","title":"make F# controller async","isDone":false,"dateAdded":"2016-09-21T19:36:44.6650776Z"},{"id":"5bb5f544-6289-4051-ad65-d0dc016128e7","title":"learn F# basics","isDone":true,"dateAdded":"2016-09-22T11:59:00"},{"id":"e5e06118-c49f-496a-8175-9719ea72beed","title":"monkey business","isDone":false,"dateAdded":"2016-09-22T16:22:20.3133161Z"},{"id":"af0db8f2-6b49-4e31-86fa-e27c8e091f42","title":"funky bidness","isDone":false,"dateAdded":"2016-09-22T16:23:35.1175195Z"}],"id":65,"exception":null,"status":5,"isCanceled":false,"isCompleted":true,"creationOptions":0,"asyncState":null,"isFaulted":false}
所以我仍然可以使用一些帮助来说明如何使F#版本产生预期的输出
答案 0 :(得分:4)
更新2016-09-28
感谢Ruben Bartelink,这就是我的控制器现在正确实现为异步并处理C#和F#异步模式之间差异的细微差别:
namespace FSharp.WebLib
open System
open Microsoft.AspNetCore.Mvc
open Microsoft.AspNetCore.Routing
open Microsoft.AspNetCore.JsonPatch
open FSharp.Models
module ActionResult =
let ofAsync (res: Async<IActionResult>) =
res |> Async.StartAsTask
[<Route("api/[controller]")>]
type FSToDoController(commands: IToDoCommands, queries: IToDoQueries) =
inherit Controller()
[<HttpGet>]
member this.Get() =
ActionResult.ofAsync <| async {
let! data = queries.GetAll()
return JsonResult(data) :> _ }
[<HttpGet("{id}", Name = "GetFsToDo")>]
member this.Get(id) =
ActionResult.ofAsync <| async {
let! res = queries.Find id
match res with
| None -> return this.NotFound() :> _
| Some data -> return ObjectResult(data) :> _ }
// create
[<HttpPost>]
member this.Post([<FromBody>] item:ToDoItem) =
ActionResult.ofAsync <| async {
if not this.ModelState.IsValid then
return this.BadRequest() :> _
else
let item = { item with Id = Guid.NewGuid() |> string }
do! commands.Add item
let rv = RouteValueDictionary()
rv.Add("id",item.Id)
return this.CreatedAtRoute("GetFsToDo", rv, item) :> _ }
// update
[<HttpPut("{id}")>]
member this.Put(id:String, [<FromBody>] item:ToDoItem) =
ActionResult.ofAsync <| async {
if (not this.ModelState.IsValid) || String.IsNullOrEmpty item.Id then
return this.BadRequest() :> _
else
let! res = queries.Find id
match res with
| None -> return this.NotFound() :> _
| Some toDo ->
do! commands.Update item
return NoContentResult() :> _ }
对于任何有兴趣学习F#特别是在ASP.NET Core中使用的人来说,这是proof of concept project on github的一部分,它同时具有ToDo列表后端Web api的C#和F#实现,两者都来自前端用聚合物网组件实现。模型和数据访问也以两种语言实现,以便为像我这样的C#开发人员学习F#
进行良好的比较