user1206480
user1206480

Reputation: 1858

Running c# async method in f# workflow

I am trying to get the below code to work in a F# async workflow, but I am getting the error "Unexpected symbol '}' in expression". I am fairly new to both F# and async in general. What am I missing here.

let someFunction (req : HttpRequestMesssage) a b = 

    // code
    async{
        let! readToProvider =
            req.Content.ReadAsMultipartAsync(provider)
            |> Async.AwaitIAsyncResult 

    } |> Async.RunSynchronously

    req.CreateResponse(HttpStatusCode.OK)

Upvotes: 4

Views: 1432

Answers (2)

Dax Fohl
Dax Fohl

Reputation: 10781

I worry that my previous answer wasn't quite what you want. What I supplied just got you through the compile error. But one thing about it, is that it does not run asynchronously. Task.Wait and Async.RunSynchronously will both block the running thread until the operation is complete.

If you want to actually be async, i.e. not blocking, you have to put the entire method, or at least the last part of it, into the async block, such that you're actually returning an async op to the caller. So the answer would be

let someFunction (req : HttpRequestMesssage) a b = 
  async {
    let! readToProvider = (req.Content.ReadAsMultipartAsync provider) |> Async.AwaitIAsyncResult 
    return req.CreateResponse HttpStatusCode.OK
  }

This option returns not the response, but an Async<Response>. So now the caller can decide how to run it, either blocking or truly asynchronously.

This way if you're using a web server that handles asynchronous requests, then you can simply connect this function to an endpoint (probably converting the Async to a Task at the point of connection, since most .net async web servers are written from C# perspective) and it'll run asynchronously without blocking a thread. Or if you're calling it from another async op you can do do! someFunction ... and it'll run asynchronously. But if the caller doesn't care and just wants to run synchronously, it can do someFunction ... |> Async.RunSynchronously. So you get more flexibility there. And you can always define let someFunctionSync ... = someFunction ... |> Async.RunSynchronously if that's the more common use case.

I'd recommend going this way unless you really want to enforce blocking.

Upvotes: 6

Dax Fohl
Dax Fohl

Reputation: 10781

You're doing it right. You're only getting the error because you're ending your async block with a let! expression. Change it to return!, or do! ... |> Async.Ignore and you'll be good.

Blocks in F# (neither workflows nor regular code blocks) should not end with let.

Of course if all you're really doing in the workflow is that one call, you don't need the workflow block at all (you never really should need to write a block for a single call). Just do

req.Content.ReadAsMultipartAsync provider
  |> Async.AwaitIAsyncResult 
  |> Async.Ignore
  |> Async.RunSynchronously
req.CreateResponse HttpStatusCode.OK

Or for that matter, just use the built in Tasks Wait, which does the same thing as Async.RunSynchronously:

(req.Content.ReadAsMultipartAsync provider).Wait()

Upvotes: 5

Related Questions