【问题标题】:What is the correct way to read db data async?异步读取数据库数据的正确方法是什么?
【发布时间】:2016-12-10 04:12:16
【问题描述】:

能否请您指出一个错误,为什么我让以下代码同步运行并在对 DB 的异步请求期间阻塞 UI?提前致谢。

我的视图模型:

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 框架的 EventToCommand 触发的 Loaded 事件:

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

【问题讨论】:

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


【解决方案1】:

您通过在此处的 Task 上调用 Result 来阻塞您的线程:

Merchants = SQLite.GetMerchants().Result;

相反,您应该等待Task。不幸的是,您无法创建构造函数async,因此您必须将该代码移动到事件处理程序,可能是Loaded 或其他东西,然后创建async

【讨论】:

  • 添加了有关问题的信息:添加了新函数并清除了构造函数。
  • 那么Window_Loaded在哪里?
  • 向问题添加信息。
【解决方案2】:

正如您所发现的,SQLite 异步方法实际上并不是异步的。所以,你需要使用像Task.Run 这样的后台线程。

可以Loaded事件中做到这一点:

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

但是,我认为使用my NotifyTask&lt;T&gt; 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}" .../>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-06-27
    • 2020-05-15
    • 1970-01-01
    • 2020-08-19
    • 1970-01-01
    • 2015-03-30
    • 2015-04-08
    • 2019-05-28
    相关资源
    最近更新 更多