异步读取数据库数据的正确方法是什么
本文关键字:方法 是什么 读取 数据库 数据 异步 | 更新日期: 2023-09-27 18:00:12
你能告诉我一个错误吗?为什么我让以下代码同步运行,并在向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仍然被阻止(视图模型构造函数现在为空):
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();
}
您通过在Task
上调用Result
来阻塞线程:
Merchants = SQLite.GetMerchants().Result;
相反,您应该等待Task
。不幸的是,您无法生成构造函数async
,因此您必须将代码移动到事件处理程序,可能是Loaded
或其他什么程序,然后生成async
。
正如您所发现的,SQLite异步方法实际上并不是异步的。因此,您需要使用像Task.Run
这样的后台线程。
您可以在Loaded
事件中执行此操作:
public async void Loaded()
{
MerchantsLoading = true;
Merchants = await Task.Run(() => SQLite.GetMerchants());
MerchantsLoading = false;
}
然而,我认为使用我的NotifyTask<T>
类型:会更干净
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}" .../>