2016-06-16 1 views
2

Я пытаюсь сделать мой базовый класс репозитория асинхронным, и у меня возникают проблемы. Я использую Dapper ORM в своем приложении C#.Блокировка основной темы на async/await

метод Base

protected async Task<List<T>> Read<T>(CommandDefinition cmd) { 
    using(SqlConnection myCon = new SqlConnection(Config.DBConnection)) { 
     await myCon.OpenAsync(); 

     IEnumerable<T> results = await myCon.QueryAsync<T>(cmd); 

     List<T> retVal = results.ToList(); 

     myCon.Close(); 

     return retVal; 
    } 
} 

Вызов метода

public List<Category> GetAllActiveCategories(Guid siteGuid) { 
    return base.Read<Category>(SPNAME_GETALLACTIVE, siteGuid).Result; 
} 

Все выглядит в порядке со мной. У меня есть объявление метода, украшенное ключевым словом async. Я жду асинхронных методов.

Проблема, с которой я столкнулась, заключается в том, что нитки блокируются на await myCon.OpenAsync();. Это моя первая попытка использовать async и ждать, поэтому я уверен, что я делаю что-то неправильно, но это не очевидно. Пожалуйста помоги!

+0

@KirillShlenskiy Я думаю, что вы на что-то. Я буду редактировать большинство, чтобы показать один из методов, которые я использую, который блокирует его. – fizch

+0

Теперь, когда вы отправили потребителя, я уверен. Отправляя мои комментарии в качестве ответа. –

ответ

3

Отправленный код чтения в порядке. Проблема заключается в коде потребления. Как правило, вы получаете тупик с async, если вы позвоните Wait() или Result по возвращенному Task или его антецеденту Task в цепочку звонков.

Как обычно, в этих случаях применяются общие рекомендации: don't block on async code. После того, как вы начнете использовать async/await, вы должны использовать async/await на протяжении всей цепочки вызовов.

Таким образом, ваш вызывающий метод становится

public Task<List<Category>> GetAllActiveCategoriesAsync(Guid siteGuid) { 
    return base.Read<Category>(SPNAME_GETALLACTIVE, siteGuid); 
} 

... или

public async Task<List<Category>> GetAllActiveCategoriesAsync(Guid siteGuid) { 
    List<Category> result = await base.Read<Category>(SPNAME_GETALLACTIVE, siteGuid); 

    // Do something. 

    return result; 
} 
+0

мой вопрос только в том, как получить доступ к результатам на самом верхнем уровне без его блокировки. – fizch

+1

@fizch, к сожалению, async имеет способ «отравить» ваш код - в том, что вам нужно переписать целую цепочку * async'. Конечно, если вы работаете с синхронной цепочкой вызовов, вы всегда можете использовать методы блокировки старой школы в вашем конкретном случае. В качестве альтернативы вы можете исправить свою проблему, используя 'ConfigureAwait (false)' для всех 'Task', ожидаемых внутри вашего метода 'Read', но, хотя это лучше всего, антипаттерн и его следует избегать. –

+0

Но я отвлекся, поскольку я больше не отвечаю на исходный вопрос, как указано. –

1

Виной:

return base.Read<Category>(SPNAME_GETALLACTIVE, siteGuid).Result; 

Как отметил Кирилл, в любое время вы используете .Wait() или .Result в задаче вы блокируете синхронно. Что вам нужно сделать, это:

public Task<List<Category>> GetAllActiveCategories(Guid siteGuid) { 
    return base.Read<Category>(SPNAME_GETALLACTIVE, siteGuid); 
} 

Это возвращает задачу к вызывающему методу в это метод, и так далее ... это должно быть асинхронной «вплоть до».

Если потребитель верхнего уровня этого кода является ASP.NET, вы в порядке. Просто верните Task<IActionResult> (или соответствующий тип возврата, завернутый в Задачу), и структура будет сортировать await ing для вас.

Если вы пишете консольное приложение или иным образом не может сделать это «асинхронной весь путь вверх», вы будете иметь либо блок на .Result или сделать свой метод async void и использовать await. К сожалению, ни одно из них не является прекрасным решением. Async/await довольно агрессивен в том смысле, что вам действительно нужно использовать его в вашем стеке.

+0

@fizch Добавлено небольшое объяснение. Ответ Кирилла (и ссылка) также является хорошей информацией! –