Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it necessary to use async keyword when using IDisposable in F#?

I get a Cannot access a disposed object error when running the following code (MyClient is a WCF client generate by a service reference in a C# project).

type Action =
    | Add
    | Update

let addStuff (myClient:MyClient) arg = async {
    let! response = myClient.AddAsync arg |> Async.AwaitTask
    return response.ID
}

let updateStuff (myClient:MyClient) arg = async {
    let! response = myClient.UpdateAsync arg |> Async.AwaitTask
    return response.ID
}

let doStuff arg =
    use myClient = new MyClient()
    match arg.Action with
    | Add -> arg |> addStuff myClient
    | Update -> arg |> updateStuff myClient

let args = [Add, Add, Update, Add]

let results =
    args
    |> List.map doStuff
    |> Async.Parallel

It seems the client is being disposed before I expect it to. If I change doStuff to:

let doStuff arg = async {
    use myClient = new MyClient()
    return!
        match arg.Action with
        | Add -> arg |> addStuff myClient
        | Update -> arg |> updateStuff myClient
}

The return type of both functions is Async<int>. Why is the client being disposed early in the first example? I would think both examples are logically identical. My understanding is that the async workflow is only necessary if you need to use the ! bindings which I do not think is necessary in this case as the actual await is happening in the specific functions.

like image 251
Ralphie Avatar asked Jan 17 '26 10:01

Ralphie


1 Answers

The problem is with doStuff:

let doStuff arg =
    use myClient = new MyClient()
    match arg.Action with
    | Add -> arg |> addStuff myClient
    | Update -> arg |> updateStuff myClient

You are passing myClient into an async function which captures the MyClient instance. However, when doStuff returns it calls Dispose on the MyClient instance and disposes the client. When your async method gets around to running it is using a disposed instance.

Making doStuff works because the dispose becomes part of the async workflow.

Another option would be to not use the MyClient instance but instean have addStuff and updateStuff create their own MyClient instance.

like image 106
Sean Avatar answered Jan 20 '26 03:01

Sean



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!