2016-06-02 2 views
2

Я использую методы async для EF для извлечения данных из базы данных. В большинстве случаев все хорошо. Я пришел в несколько ObjectContextDisposed исключений в последнее время, и мне интересно, почему мое решение работает:Разница между этими двумя реализациями асинхронизации

Вот мой исходный код, который бросил ObjectContextDisposed:

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

Вот мой новый код, который Безразлично» 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; 
    } 
} 

Может кто-нибудь объяснить мне, в чем разница, и почему это работает?

Просто FYI, во всех моих использованиях этого метода, я называю await GetEventParameterMru()

Благодаря

ответ

5

GetEventParameterMRU по-видимому, метод, который начинает Task получить некоторые данные. Таким образом, GetEventParameterMRU возвращается до завершения всех операций на repo.

В обеих версиях вашего кода используется оператор using, который преобразуется в блок try/finally. В блоке finallyrepo будет размещено.

В вашей первой версии вы возвращаете немедленно после звонка GetEventParameterMRU (начиная задачу). Это означает, что repo расположен непосредственно, а Task с использованием repo все еще работает.Поэтому, когда этот Task доступ repo вы получите ваш

ObjectDisposedException


Во втором варианте вы используете await. Компилятор для этого переводит весь ваш метод в конечный автомат . Метод возвращает управление своему вызывающему абоненту в операторе await, но без передает finally блок.
Когда завершается Task, выполнение вашего метода продолжается после инструкции await.
Таким образом, repo является , только когда Task завершен. Таким образом, вы не получаете ObjectDisposedException.

+0

Спасибо, это было очень ясно и красно. – Simon

2

После мое понимание на первоначальном обзоре коды:

В первом, которая не является Async реализацией, но все же задача возвращается из using block, которая в конце вызовет repo.Dispose(), когда, наконец, Task is executed (при условии, что ее выполнил вызывающий объект, но верно и для метода - GetEventParameterMRU, начиная с него), это вполне осуществимо кий, что репо расположен к тому времени, но все же операция, и, таким образом, исключение

В втором, что это не том случае, даже когда он не освободил UI/вызывающий поток, до и если следующим завершается:

await repo.GetEventParameterMRU(_CurrentWorkpack, parameter, count) 

это не будет вызывать repo.Dispose() и, таким образом, полностью избежать ObjectContextDisposed вопроса

+0

Почему 'repo.Dispose();' не будет вызываться во втором? Я уверен, что так будет. Никакой код не устранен, потому что он асинхронный. – Maarten

+0

@Maarten Он всегда называется. Проблема в том, КОГДА она называется. – Euphoric

+0

@Euphoric Я знаю, что именно этот ответ предполагает, что метод Dispose не вызывается во втором примере. – Maarten

0

Я считаю, что это должно сделать с первым кода не захватывая текущее SynchronizationContext явно, в то время как ожидает ДЕЛАЕТ захватить это явно.

Некоторые дальнейшего чтения:

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

+0

Эта проблема не возникает из-за не захватывая контекст синхронизации, это будет объект становится когда все еще используется –

1

Позвольте мне перевести их для вас:

Первый один:

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

Второй один:

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

Как вы можете видеть, что проблема вполне ясно.