Simon
Simon

Reputation: 9425

Difference between these two async implementations

I am using EF's async methods to fetch data from a database. Most of the time all is well. I have come into a few ObjectContextDisposed exceptions of late, and I am curious as to why my solution works:

Here was my original code that threw ObjectContextDisposed:

public Task<List<string>> GetEventParameterMru(EventParameter parameter, int count = 20)
{
    using (var repo = new ConfigurationRepository())
    {
        return repo.GetEventParameterMRU(_CurrentWorkpack, parameter, count)             
    }
}

Here is my new code that doesn't throw:

public async Task<List<string>> GetEventParameterMru(EventParameter parameter, int count = 20)
{
    using (var repo = new ConfigurationRepository())
    {
        var result = await repo.GetEventParameterMRU(_CurrentWorkpack, parameter, count);
        return result;
    }
}

Could someone explain to me what the difference is, and why it works?

Just FYI, in all my usages of this method, I call await GetEventParameterMru()

Thanks

Upvotes: 2

Views: 70

Answers (4)

Euphoric
Euphoric

Reputation: 12849

Let me translate them for you:

First one:

Create repository
Start GetEventParameterMRU on repository as Task
Dispose repository
return Task, that is still working with repository

Second one:

Create repository
Start GetEventParameterMRU on repository as Task
Wait for Task to finish to get result
Dispose repository
return result

As you can see, the problem is quite clear here.

Upvotes: 1

Ren&#233; Vogt
Ren&#233; Vogt

Reputation: 43916

GetEventParameterMRU is apparently a method that starts a Task to retrieve some data. So GetEventParameterMRU returns before all operations on the repo have been completed.

Both version of your code use a using statement, which is translated into a try/finally block. In the finally block, repo will be disposed.

In your first version, you return immediatly after calling GetEventParameterMRU (starting the task). That means that repo is disposed immediatly while the Task using repo is still running. So when this Task accesses repo you receive your

ObjectDisposedException


In the second version you use await. The compiler therefor translates your whole method into a state machine. The method returns control to its caller at the await statement, but without passing the finally block.
When the Task completes, execution of your method is continued after the await statement.
So repo is only disposed when the Task has completed. So you don't get an ObjectDisposedException.

Upvotes: 5

BlakeH
BlakeH

Reputation: 3494

I believe this has to do with the first code not capturing the current SynchronizationContext explicitly, while await DOES capture this explicitly.

Some further reading:

https://msdn.microsoft.com/en-us/magazine/gg598924.aspx

Upvotes: 0

Mrinal Kamboj
Mrinal Kamboj

Reputation: 11478

Following is my understanding on initial code review:

In first one, which is not Async implementation but still a Task is returned from the using block, which would call the repo.Dispose() in the end, when finally Task is executed (assuming its done by the caller, but holds true even for the method - GetEventParameterMRU starting it), it's quite feasible that repo is disposed by that time, but still operation is on and thus the exception

In second one, that's not the case, even when it has freed the UI / Calling thread, until and unless following completes:

await repo.GetEventParameterMRU(_CurrentWorkpack, parameter, count)

It will not call repo.Dispose() and thus completely avoid the ObjectContextDisposed issue

Upvotes: 2

Related Questions