Reputation: 9425
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
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
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
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
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