读取db数据异步的正确方法是什么?

时间:2016-08-04 07:02:47

标签: c# .net wpf sqlite async-await

你能不能指责我为什么我有以下代码同步运行并在异步请求到数据库期间阻止UI的错误?提前谢谢。

我的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);
}

由DevExpress MVVM Framework的EventToCommand触发的加载事件:

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

2 个答案:

答案 0 :(得分:3)

您正在Result点击Task

来阻止您的主题
Merchants = SQLite.GetMerchants().Result;

相反,你应该等待Task。遗憾的是,您无法创建构造函数async,因此您必须将该代码移动到事件处理程序,可能是Loaded或其他内容,并将其async

答案 1 :(得分: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()));
}

with view:

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