2016-09-21 3 views
2

Ich bin neu zu F # und versuchen, einige C# ASP.NET Core-Code in F # übersetzenF # Syntax für Asynchron-Controller-Methoden in ASP.NET-Core

Es gibt einen Controller C# ist here, und eine Arbeits übersetzt F # Controller here

Obwohl ich es funktioniert habe, kann ich nicht herausfinden, wie die Controller-Aktionen async zu machen. Die Methoden rufen einen asynchronen Code für ein Commands-Objekt und ein Queries-Objekt auf, die injiziert werden. Die Befehle und Abfragen sind derzeit in C# implementiert.

So zum Beispiel ein paar der Asynchron-C# Controller-Methoden sind:

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); 
    } 

und ich habe diejenigen zu F # wie folgt übersetzt:

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

Diese Methoden funktionieren, aber ich denke, sie sind nicht korrekt ausgeführt, da sie die asynchronen Aufrufe von Abfragen und Befehlen nicht erwarten. Ich habe einige Stunden mit Versuch und Irrtum geprügelt, aber jeder Versuch, die Controller-Methoden asynchron zu machen, führt dazu, dass sie keine Daten an den Browser zurücksenden, obwohl sie einen 200-Statuscode zurückgeben. Sie können sehen, einige meiner Versuche auskommentiert in der F# controller

Ich hoffe, einige F # Guru (s) könnte mir helfen, diese Methoden korrekt zu übersetzen. Es gibt einige ziemlich schlechte Tooling-Probleme in Bezug auf F # mit ASP.NET Core, was es für einen Neuling wie mich schwieriger macht. Ich habe diese Probleme in den Code erwähnt, aber ich denke, wenn ich lernen kann, für diese Methoden zu lösen, dann wird die gleiche Lösung wahrscheinlich für die anderen Methoden gelten.

Der Code befindet sich in einem öffentlichen Repository, so dass Sie es problemlos in VS 2015 ausprobieren können, solange Sie über die neuesten VS-Updates und den neuesten ASP verfügen.NET Core-Werkzeuge installiert

UPDATE:

dank der verlinkten Beitrag von Mark Seemann, ich war in der Lage, diese Methode zu bekommen arbeiten async

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

unter Verwendung der Hilfsfunktion

let asyncReturn x = async { return x } 

Ich kämpfe immer noch mit dieser Methode

[<HttpGet>] 
member __.Get() = 
    async { 
      let! data = __.Queries.GetAll() |> asyncReturn 
      return data } 
     |> Async.StartAsTask 

, die aus dieser C# Methode übersetzt:

[HttpGet] 
public async Task<IEnumerable<ToDoItem>> Get() 
{ 
    return await queries.GetAll(); 
} 

das # Methode funktioniert Asynchron-F, aber es unterschiedlichen json Ausgang als die C# -Version

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} 

so noch produziert ich, wie man etwas Hilfe gebrauchen könnte machen die F # Version die erwartete Ausgabe

+3

versuchen Async.AwaitTask – Ringil

+1

Sie können ein Beispiel für eine asynchrone Controller-Aktion gegen Ende dieses Blog-Post sehen: http : //blog.ploeh.dk/2016/04/11/async-as-surrogate-io Hilft das? –

+0

@ mark-seemann danke! Ich habe ein paar Fortschritte dank Ihrer Post, und habe meine Frage aktualisiert in der Hoffnung auf weitere Erkenntnisse –

Antwort

2

AKTUALISIERT 2016-09-28

Dank Ruben Bartelink produzieren das ist was mein Controller wie jetzt korrekt implementiert, wie async sieht und die Nuancen der Handhabung zwischen C# und F # Asynchron-Muster unterscheiden:

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ür jemand anderes in das Lernen F # besonders für den Einsatz in ASP.NET Core ist dieser Teil a proof of concept project on github, die sowohl C# - als auch F # -Implementierungen eines ToDo-Listen-Back-End-Web-API aufweist, die beide von einem Front-End verbraucht werden, das mit Polymerbahnkomponenten implementiert ist. Modelle und Datenzugriff sind auch in beiden Sprachen implementiert, um einen guten Vergleich für C# Entwickler wie mich zu geben F #

+0

können Sie die Methoden als ': IActionResult' deklarieren und das':> IActionResult' in den Körpern durch ':> _' ersetzen . Für '(__. Commands.Add (item)) |> asyncReturn |> ignore' sollten Sie einfach' do! __. Commands.Add (item) '(wenn es' Async 'zurückgibt, oder' do! 'Mit' |> ignore' oder 'let! _ = Cmd ...'. IOW geht nicht die Neuausgabe von 'asyncReturn' –

+0

danke @RubenBartelink! Ich habe meine Antwort mit Verbesserungen basierend auf Ihren Vorschlägen aktualisiert, aber konnte es nicht kompilieren, wenn ich die vorgeschlagene Änderung über die Methoden als IActionResult deklariere, vielleicht habe ich es falsch gemacht, ich habe eine Verbindung hergestellt um meinen Versuch zu zeigen, –

+0

das ist, weil ich es nicht durchdacht hatte.Das Ergebnis sollte Task sein, damit das ':> _' funktioniert.Auch wenn Sie das 'This' verwenden, nennen Sie es nicht' __' :). Und 'Option.ofObj' ist der beste OOTB (F # 4) Weg, um mit einem if um ein' null' umzugehen (und es verallgemeinert sich besser, wenn Sie zB eine Validierung mit http://fsharpforfunandprofit.com/rop durchführen). Und 'tu! Async.AwaitTask (__. Commands.Add (item)) 'ist besser geschrieben' do! this.Commands.Add item |> Async.AwaitTask'. Wenn du ein sln mit einem fsproj auf github steckst, bin ich glücklich zu polieren. IIRC panesofglass hat eine F # ToDoMvc impl lohnt sich auch zu betrachten –