2016-08-04 4 views
0

не могли бы вы быть настолько добрыми, чтобы указать мне на ошибку, почему у меня следующий код работает синхронно и блокирует пользовательский интерфейс во время асинхронного запроса к БД? Заранее спасибо.Каков правильный способ чтения dns-данных async?

Мои ViewModel:

public virtual bool MerchantsLoading { get; set; } 
public virtual ObservableCollectionCore<Merchant> Merchants { get; set; } 

public MerchantViewModel { //constructor 
    MerchantsLoading = true; 
    Merchants = SQLite.GetMerchants().Result; 
    SQLite.GetMerchants().ContinueWith(task => MerchantsLoading =  false); 
} 

Мой Вид:

... 
<dxg:GridControl ShowLoadingPanel="{Binding MerchantsLoading}" ItemsSource="{Binding Merchants}".../> 
... 

SQLite.GetMerchants():

public static async Task<ObservableCollectionCore<Merchant>> GetMerchants() 
{ 
    SQLiteConnection SqlConnection = new SQLiteConnection(MerchantDB); 
    var Merchants = new ObservableCollectionCore<Merchant.Merchant>(); 
    try 
    { 
     await SqlConnection.OpenAsync(); 
     SQLiteCommand myCommand = new SQLiteCommand("select * from merchant", SqlConnection); 
     DbDataReader myReader = await myCommand.ExecuteReaderAsync(); 
     while (myReader.Read()) 
     { 
      Merchants.Add(new Merchant.Merchant 
      { 
       ID = Convert.ToInt32(myReader["ID"]), 
       Name = Convert.ToString(myReader["Name"]) 
      }); 
     } 
    } 
    catch (Exception ex) 
    { 
     Messenger.Default.Send(new LogMessage { Message = "Ошибка в процедуре GetMerchants" }); 
     Messenger.Default.Send(new LogMessage { Message = ex.ToString() }); 
    } 
    finally 
    { 
     SqlConnection.Close(); 
    } 
    return Merchants; 
} 

Я добавил новую функцию, которая обстреливали UserControl.Loaded событие, но UI по-прежнему заблокирован (конструктор viewmodel теперь пуст):

public async void Loaded() 
{ 
    MerchantsLoading = true; 
    Merchants = await SQLite.GetMerchants(); 
    await SQLite.GetMerchants().ContinueWith(task => MerchantsLoading = false); 
} 

Loaded событие обстреляно EventToCommand из DevExpress MVVM Framework:

<UserControl xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm" xmlns:ViewModels="clr-namespace:ORBKWorker.Merchant" 
     xmlns:Helpers="clr-namespace:ORBKWorker.Helpers" 
     xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid" 
     xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors" 
     xmlns:dxlc="http://schemas.devexpress.com/winfx/2008/xaml/layoutcontrol" 
     x:Class="ORBKWorker.Merchant.MerchantView" 
     mc:Ignorable="d" 
     DataContext="{dxmvvm:ViewModelSource Type={x:Type ViewModels:MerchantViewModel}}" 
     d:DesignHeight="800" d:DesignWidth="1920"> 
<UserControl.Resources> 
    <Helpers:IntToEmailType x:Key="IntToEmailType"/> 
</UserControl.Resources> 
<dxmvvm:Interaction.Behaviors> 
    <dxmvvm:EventToCommand EventName="Loaded" Command="{Binding LoadedCommand}"/> 
</dxmvvm:Interaction.Behaviors> 
<Grid>... 

Наконец решил сделать это с BackgroundWorker:

public void GetMerchants() 
    { 
     MerchantsLoading = true; 
     BackgroundWorker bgw = new BackgroundWorker(); 
     bgw.DoWork += (sender, args) => Merchants = SQLite.GetMerchants(); 
     bgw.RunWorkerCompleted += (sender, args) => MerchantsLoading = false; 
     bgw.RunWorkerAsync(); 
    } 
+0

Насколько я знаю, ваша ожидающая функция должна вернуть Task, t hat ждет, когда функция возвращает функцию Task. http://blog.stephencleary.com/2012/02/async-and-await.html – AnjumSKhan

ответ

3

Вы блокируете поток, вызывая Result на Task здесь :

Merchants = SQLite.GetMerchants().Result; 

Вместо этого вы должны подождать Task. К сожалению, вы не можете сделать конструктор async, поэтому вам нужно переместить этот код в обработчик события, может быть Loaded или что-то еще, и сделать это async.

+0

Добавлена ​​информация по этому вопросу: Добавлена ​​новая функция и очищенный конструктор. – Smilley

+0

Итак, где же 'Window_Loaded'? –

+0

Добавлена ​​информация к вопросу. – Smilley

0

Как вы обнаружили, асинхронные методы SQLite на самом деле не асинхронны. Итак, вам нужно использовать фоновый поток, например Task.Run.

Вы можете сделать это в Loaded события:

public async void Loaded() 
{ 
    MerchantsLoading = true; 
    Merchants = await Task.Run(() => SQLite.GetMerchants()); 
    MerchantsLoading = false; 
} 

Однако, я думаю, было бы чище использовать my NotifyTask<T> type:

public virtual NotifyTask<ObservableCollectionCore<Merchant>> Merchants { get; set; } 

public MerchantViewModel() 
{ 
    Merchants = NotifyTask.Create(Task.Run(() => SQLite.GetMerchants())); 
} 

с видом:

<dxg:GridControl ShowLoadingPanel="{Binding Merchants.IsNotCompleted}" ItemsSource="{Binding Merchants.Result}" .../>