2015-09-29 5 views
2

Я экспериментирую с асинхронным ожиданием, и я сталкиваюсь с блокировкой пользовательского интерфейса, которое не должно происходить.WPF UI блокирует в ожидании DbContext

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 

     LoadButton.Click += LoadButton_OnClick; 
    } 

    private async void LoadButton_OnClick(object sender, RoutedEventArgs e) 
    { 
     LoadButton.IsEnabled = false; 

     // await Task.Delay(2000); 

     using(TestContext ctx = new TestContext()) 
     { 
      IList<User> users = await ctx.Users.ToListAsync(); 
     } 

     LoadButton.IsEnabled = true; 
    } 
} 

Если я комментирую бит DbContext и раскомментируйте Task.Delay, он ведет себя так, как и ожидалось - не блокирует пользовательский интерфейс.

Основываясь на моем понимании, метод ToListAsync() по-прежнему вызывается из потока пользовательского интерфейса, но не должен блокировать его. Даже если метод связан с ЦП (возможно, нет), это вызовет отставание, а не полный блок.

Мои вопросы:

Правильно ли я это понимаю?

Почему мой пользовательский интерфейс блокируется в ожидании onListAsync()?


EDIT

Я пытался делать вызов базы данных перед вызовом этого метода, чтобы прогреть все и убедиться, что он не блокирует о создании первого соединения. Также я попытался добавить пару тысяч записей в DbSet и ждать SaveChangesAsync, и то же самое происходит - пользовательский интерфейс полностью замораживается в течение нескольких секунд.


EDIT 2

Я попробовал еще один пример с тем же кодом и, кажется, работает. Разница в том, что в первом примере я использую Code First и SQL CE и в рабочем примере Database First и SQL Server.

+0

'Task.Delay (2000)', конечно, заблокирует код в течение 2 секунд. Вы должны объяснить, почему вы хотите отложить на 2 секунды. – Hopeless

+0

@Hopeless он тестирует, что пользовательский интерфейс не блокируется в ожидании. Моделирование задачи, связанной с процессором –

+0

@alexw, вы имеете в виду, что он включил эту строку для проверки? – Hopeless

ответ

1

Объекты соединения, используемые в SQL Server CE, не являются threadsafe (естественно асинхронные API не реализованы). Я полагаю, что вы столкнулись с этой проблемой с SQL CE, поскольку она выполняется в потоке, который создал DbContext и не возвратил управление обратно в поток пользовательского интерфейса при ожидании. Попробуйте создать экземпляр DbContext в пределах Task и в ожидании результата.

LoadButton.IsEnabled = false; 

var users = await Task.Run(() => 
{ 
    using(TestContext ctx = new TestContext()) 
    { 
     return ctx.Users.ToList(); 
    } 
}); 

LoadButton.IsEnabled = true; 
+0

Я считаю, что безопасность потока не имеет ничего общего с этим примером, поскольку все работает в одном и том же потоке (UI). Я знаю, как исправить эту проблему, поставив очередь на работу с пулом потоков, но дело в достижении неблокирующего поведения без дополнительных потоков. – loodakrawa

+0

@LukaSverko да именно поэтому ваш поток пользовательского интерфейса блокируется. Поставщик SQL Server для Entity Framework 6 реализовал естественно асинхронные методы, которые возвращают управление обратно в поток пользовательского интерфейса при ожидании, у поставщика SQL Server CE нет. Если вы его реализуете таким образом, это обойдется в ограничениях SQL Server CE, и ваш поток пользовательского интерфейса не будет блокироваться. –

0

Я считаю, что пользовательский интерфейс заблокирован этой линии

using(TestContext ctx = new TestContext()) 

Попробуйте что-то вроде этого (просто чтобы проверить это проблема)

await Task.Run(() => {using(TestContext ctx = new TestContext()) 
    { 
     IList<User> users = ctx.Users.ToList(); 
    }}) 
+0

Это не заблокировано этой строкой. Я попытался помещать контекст в качестве члена класса и создавать его, прежде чем выполнять какую-либо работу и повторно использовать ее для каждого запрос. – loodakrawa

-2

await будет работать только с функциями, определенными с async ключевого слова и есть Task как есть возврат type.

В первом случае Task.Delay(2000) будет возвращать Timespan, поэтому ждать не будет никакого воздействия.

В то время как во втором сценарии ctx.Users.ToListAsync() возвращает задачу, и после этого мы вызываем ожидание, что приводит к блокировке потока текущего (UI) потока.

Для получения дополнительной информации см. link от Microsoft.

+0

Я не думаю, что жду ctx.Users.ToListAsync() будет блокировать поток пользовательского интерфейса. – cscmh99

+3

Этот ответ так, так неправильно –

2

Кажется, что SQL Server Compact ADO.NET провайдер не предоставляет асинхронные API, и именно поэтому он блокирует. Я не могу найти точный источник об этом, но this answer четко заявляет об этом.

+0

Да, пожалуйста, см. Мой ответ о том, как вы можете обойти это, выполнив запросы в отдельном потоке. –

 Смежные вопросы

  • Нет связанных вопросов^_^