2016-02-11 3 views
3

Я пытаюсь познакомиться с асинхронным/ждущим. Поэтому я пытаюсь написать программу C#/WPF для асинхронного запроса базы данных без блокировки моего графического интерфейса.async функция блокирует мое приложение wpf

Я создал объект, реализующий интерфейс INotifyPropertyChanged. Этот объект предлагает свойство DataTable, и это должно измениться функцией async. Мой компонент GUI имеет привязку к свойству DataTable.

Мой объект выглядит так:

public class AsyncDataDemo : INotifyPropertyChanged 
{ 
    protected DataTable data = new DataTable(); 

    public DataTable Data 
    { 
     get { return data; } 
     protected set 
     { 
      data = value; 
      doPropertyChanged("Data"); 
     } 
    } 

    protected virtual void doPropertyChanged(string propertyName) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChangedEventArgs Arguments = new PropertyChangedEventArgs(propertyName); 
      PropertyChanged(this, Arguments); 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected async Task<DataTable> OpenQueryAsync(string ConnectionString, string Query) 
    { 
     OdbcConnection connection = new OdbcConnection(ConnectionString); 
     await connection.OpenAsync().ConfigureAwait(false); 

     OdbcCommand command = new OdbcCommand(Query, connection); 
     DbDataReader dataReader = await command.ExecuteReaderAsync().ConfigureAwait(false); 

     DataTable resultData = new DataTable(); 
     resultData.Load(dataReader); 
     connection.Close(); 
     return resultData; 
    } 

    public async void RunQueryAsync(string Query) 
    {   
      Data = await OpenQueryAsync("<ConectionString>", (Query as string)).ConfigureAwait(false);   
    } 
} 

И в случае нажатия кнопки я называю:

private void Button_Click(object sender, RoutedEventArgs e) 
{ 
    data.RunQueryAsync("SELECT * FROM BigTable"); 
} 

Это прекрасно работает с одним исключением: нажатие кнопки блокирует мой графический интерфейс пользователя, пока данные не и я не понимаю, почему.

Может кто-нибудь, пожалуйста, объясните мне свою неудачу? Я не понимаю, почему асинхронная функция не будет выполняться асинхронно?

С уважением

ответ

5

Вам действительно нужно пошагово с отладчиком, чтобы выяснить, какой шаг занимает много времени. Наиболее вероятным кандидатом является resultData.Load(dataReader);, как ваш код все еще может быть в потоке пользовательского интерфейса, если каждая вызванная вами функция возвращает задание, которое уже имеет .IsCompleted == true.

Если задача уже завершена, и вы await, вы останетесь на границе пользовательского интерфейса, хотя вы сделали ConfigureAwait(false). Все, что должно произойти: OpenAsync() и ExecuteReaderAsync() необходимо выполнить очень быстро или полностью синхронно (оба очень возможно).

Один из способов исправить это положил запрос на фоновый поток, чтобы начать, а не ждать на ConfigureAwait(false), чтобы сделать это за вас.

public async void RunQueryAsync(string Query) 
{   
     Data = await Task.Run(() => OpenQueryAsync("<ConectionString>", (Query as string)));   
} 

Я также удалил ConfigureAwait(false), потому что вы хотите, чтобы ваш INotifyPropertyChanged произойдет в потоке пользовательского интерфейса.

Кроме того, вам действительно нужно распоряжаться своими одноразовыми объектами.

protected async Task<DataTable> OpenQueryAsync(string ConnectionString, string Query) 
{ 
    using(OdbcConnection connection = new OdbcConnection(ConnectionString)) 
    {  
     await connection.OpenAsync().ConfigureAwait(false); 

     using(OdbcCommand command = new OdbcCommand(Query, connection)) 
     using(DbDataReader dataReader = await command.ExecuteReaderAsync().ConfigureAwait(false)) 
     { 
      DataTable resultData = new DataTable(); 
      resultData.Load(dataReader); 
      return resultData; 
     } 
    } 
} 
+0

Спасибо, что сделал трюк. А также благодарю вас за намек на мои одноразовые предметы. Я сделаю лучше. – Mike

5

Причина поведения вы испытываете недостаток в DbConnection и DbCommand классов, которые все ADO провайдеры используют в качестве основы для их конкретных классов. И недостатком является то, что по умолчанию все методы Async: синхронно! Это даже задокументировано!

Например, документация для DbConnnection.OpenAsync:

Реализация по умолчанию вызывает Open вызов синхронное и возвращает завершенную задачу.

и DbCommand.ExecuteDbDataReaderAsync:

Реализация по умолчанию вызывает метод ExecuteReader синхронное и возвращает завершенную задачу, блокирует вызывающий поток.

Из того, что я видел, только поставщик SqlServer переопределяет асинхронные методы с реальной асинхронной реализацией. Но поскольку вы используете провайдера OleDb, вам не повезло.

+0

Спасибо за очень полезное объяснение, я буду помнить об этом. – Mike

+0

@Mike В случае непонятности (объединение информации в двух ответах), когда асинхронная функция завершает синхронно выполнение '.ConfigureAwait (false)' не влияет на поток кода, и вы все равно будете в пользовательском интерфейсе нить после вызова 'await', даже если вы установили' .ConfigureAwait (false) '. –

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