2016-09-21 46 views
2

我是新來的F#,並試圖翻譯一些C#ASP.NET核心代號爲F#在ASP.NET異步控制器方法F#的語法核心

有一個C#控制器here和工作翻譯F#控制器here

即使我得到它的工作,我似乎無法弄清楚如何使控制器操作異步。這些方法在注入的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可以幫助我正確地翻譯這些方法。目前在F#和ASP.NET Core方面存在一些非常糟糕的工具問題,這使得像我這樣的新手變得更加困難。我在readme

提到的這些問題中有代碼的一些額外的方法,但我想如果我能學會如何解決這些方法,然後同樣的解決方案將可能適用於其他方法。

該代碼位於公共存儲庫中,因此只要您擁有最新的VS更新和最新的ASP,您就可以在VS 2015中輕鬆進行試用。NET核心工具安裝

UPDATE:

感謝鏈接後由馬克·西曼,我能夠通過使用輔助函數

let asyncReturn x = async { return x } 
得到這個方法異步

[<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 

工作

我仍在努力用這種方法

[<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#方法的作品,但它產生比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} 

不同的JSON輸出,所以我仍然可以使用上如何一些幫助使F#版本能產生預期的輸出

+3

嘗試Async.AwaitTask – Ringil

+1

您可以在此博客末尾看到一個異步控制器操作示例:http ://blog.ploeh.dk/2016/04/11/async-as-surrogate-io這有幫助嗎? –

+0

@ mark-seemann謝謝!我已經得到了一些進展感謝您的帖子,並已更新我的問題,希望進一步的洞察 –

回答

2

已更新2016年9月28日

由於魯本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核心使用,這是一部分一個proof of concept project on github,它具有ToDo列表後端web api的C#和F#實現,這兩個實例都是從用聚合物web組件實現的前端消耗的。模型和數據訪問也使用這兩種語言實現,以便爲像我這樣的C#開發人員提供一個很好的對比來學習F#

+0

你可以聲明方法爲':IActionResult'並用':> _'替換體內的':> IActionResult' 。對於'(__。Commands.Add(item))|> asyncReturn |> ignore',你應該只能'做! __。Commands.Add(item)'(如果它返回'Async '或者'do!'with'|> ignore'或者'let!_ = cmd ...'。IOW不要下載'asyncReturn' –

+0

謝謝@RubenBartelink!我已經根據你的建議更新了我的答案,但是如果我提出有關將方法聲明爲IActionResult的建議更改,則無法編譯它,也許我做的不正確,我鏈接了一個要顯示我的嘗試 –

+0

這是因爲我沒有想到它的結果應該是任務爲':> _'工作。另外如果你真的使用'this',不要叫'__' :)。而'Option.ofObj'則是處理'null'(如果你使用http://fsharpforfunandprofit.com/rop進行驗證)的情況下最好的OOTB(F#4)方法。 「做! Async.AwaitTask(__。Commands.Add(item))'寫得更好'do! this.Commands.Add item |> Async.AwaitTask'。如果你在github上用fsproj粘貼sln,我很樂意擦亮。 IIRC panesofglass有一個F#ToDoMvc impl也值得一看 –

相關問題