2016-08-04 27 views
0

Könntest du bitte so freundlich sein, mich auf den Fehler hinzuweisen, warum ich den folgenden Code synchron laufen lasse und UI bei asynchronen Anfragen an DB blockiere? Danke im Voraus.Wie kann man db-Daten asynchron lesen?

Meine 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); 
} 

Meine Ansicht:

... 
<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; 
} 

ich neue Funktion hinzugefügt haben, die auf UserControl.Loaded gebrannt Ereignis, aber UI ist immer noch blockiert (Viewmodel-Konstruktor ist jetzt leer):

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

Loaded-Ereignis ausgelöst durch EventToCommand von 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>... 

Endlich ist es mit Background zu tun beschlossen:

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

Nach meinem Wissen sollte Ihre wartende Funktion Aufgabe, t Hut ist erwarten sollte mit einer Funktion verwendet werden, um eine Aufgabe zurückzugeben. http://blog.stephencleary.com/2012/02/async-and-await.html – AnjumSKhan

Antwort

3

Sie werden von Ihrem Thread blockiert Result auf dem Task hier Aufruf :

Merchants = SQLite.GetMerchants().Result; 

Stattdessen sollten Sie die Task erwarten. Leider können Sie keinen Konstruktor async machen, also müssen Sie diesen Code in einen Event-Handler verschieben, vielleicht Loaded oder so, und machen Sie das async.

+0

Informationen zur Frage hinzugefügt: Neue Funktion hinzugefügt und gelöscht Konstruktor. – Smilley

+0

Wo ist 'Window_Loaded'? –

+0

Informationen zur Frage hinzugefügt. – Smilley

0

Wie Sie festgestellt haben, sind die asynchronen SQLite-Methoden nicht wirklich asynchron. Sie müssen also einen Hintergrund-Thread wie Task.Run verwenden.

Sie können tun es in der Loaded Veranstaltung:

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

Aber ich denke, es wäre sauberer sein my NotifyTask<T> type zu verwenden:

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

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

mit Aussicht:

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