gpt4 book ai didi

c# - 如何将 async/await 与返回 ObservableCollection 的方法一起使用

转载 作者:行者123 更新时间:2023-11-30 14:25:13 25 4
gpt4 key购买 nike

我正在使用 MVVM 模式编写 WPF 应用程序。我必须说我(还)不熟悉 .NET 异步编程模型(只是学习它),但多年来一直在用 C/C++ 和 Java 编写多线程应用程序。

我的想法是在与 UI 线程不同的线程中启动长时间运行的查询,这样我就有机会在操作运行时显示微调器或进度条。

我的大部分内部服务方法返回集合或其他复杂类型,如 DTO 等。结果通常绑定(bind)到用户控件,如 GridView、ListView 等。我总是收到编译器错误,说我的返回类型不包含定义'获取服务员'。所以我一定是做错了什么。

下面是我的 ViewModel 中此类方法的示例,它调用执行数据库查询并返回 ObservableCollection 的服务。

    private async void GetInvoices()
{
IsOperationRunning = true; //Binding for a RadBusyIndicator
Invoices = await _uiContractService.GetInvoices(SelectedInvoiceState, SelectedSupplierItem.Id, SelectedInvoiceDateFilter, FilterStartDate, FilterEndDate);
IsOperationRunning = false;
RaisePropertyChanged(() => Invoices);
if (Invoices.Count == 0)
SendUserInfoMsg($"NO invoices matched your search criteria.", UiUserNotificationMessageType.Warning);
}

这种情况下的编译器错误是: ObservableCollection<InvoiceDto>' does not contain a definition for 'GetAwaiter' and no extension method 'GetAwaiter' accepting a first argument of type 'ObservableCollection<InvoiceDto>' could be found

Invoices 集合和服务声明为

    public ObservableCollection<InvoiceDto> Invoices { get; set; }
var _uiContractService = SimpleIoc.Default.GetInstance<UiContractService>();

服务方法的签名如下:

public ObservableCollection<InvoiceDto> GetInvoices(InvoiceState invoiceState, Guid supplierId, UiInvoiceDateFilters dateType, DateTime begin, DateTime end)

非常感谢任何能引导我走向正确方向的帮助。

最佳答案

was writing multi-threaded applications in C/C++ and Java for many years.

这种体验可能会误导您。异步代码与多线程代码非常不同。

await 与线程无关。或者换句话说,它不会增加使用的线程数;它减少它们。

我建议您先阅读我的 async intro了解 async/await 实际做了什么。

然后,您可以采用以下两种方式之一。第一种方法是理想的,但却是您实际要求的:

The idea is to start long running queries in a separate thread from the UI thread so I have a chance to display a spinner or progress bar while the operation is running.

第一种方法是多线程。在这种情况下,您可以使用 async/await 作为从另一个线程上运行的操作检索结果的便捷方式(包括干净、正确的异常堆栈):

private async Task GetInvoicesAsync()
{
IsOperationRunning = true; //Binding for a RadBusyIndicator
Invoices = await Task.Run(() => _uiContractService.GetInvoices(SelectedInvoiceState, SelectedSupplierItem.Id, SelectedInvoiceDateFilter, FilterStartDate, FilterEndDate));
IsOperationRunning = false;
RaisePropertyChanged(() => Invoices);
if (Invoices.Count == 0)
SendUserInfoMsg($"NO invoices matched your search criteria.", UiUserNotificationMessageType.Warning);
}

请注意,引入多线程的是 Task.Run - 它将工作安排到线程池。 async/await 是 UI 线程如何假装操作是异步的。

这可以从事件处理程序(或 ICommand)调用:

public async void EventHandler(object sender, ...)
{
await GetInvoicesAsync();
}

请注意,只有事件处理程序应该是async void。看我的article on async best practices获取更多信息。


但第一种方法并不理想,因为它不是真正的异步。这就是我所说的“伪异步”;也就是说,UI 假装它是异步的,但实际操作只是同步阻塞另一个线程。

在编写真正的异步代码时,从其他端开始会更容易 - 不是顶层 UI 方法,而是底层实际 API 调用。如果您的服务是 HTTP 服务,请查看 HttpClient;如果它们是 WCF 服务,请重新生成启用异步 API 的代理。

一旦您拥有可用的最低级别异步 API,就开始使用它们。最终,您将得到一个异步的 GetInvoices:

public async Task<List<InvoiceDto>> GetInvoicesAsync(InvoiceState invoiceState, Guid supplierId, UiInvoiceDateFilters dateType, DateTime begin, DateTime end);

(请注意,我从您的服务层中删除了 ObservableCollection,因为它是面向 UI 的类型)

然后你可以这样调用这个方法:

private async Task GetInvoicesAsync()
{
IsOperationRunning = true; //Binding for a RadBusyIndicator
Invoices = new ObservableCollection<InvoiceDto>(await _uiContractService.GetInvoicesAsync(SelectedInvoiceState, SelectedSupplierItem.Id, SelectedInvoiceDateFilter, FilterStartDate, FilterEndDate));
IsOperationRunning = false;
RaisePropertyChanged(() => Invoices);
if (Invoices.Count == 0)
SendUserInfoMsg($"NO invoices matched your search criteria.", UiUserNotificationMessageType.Warning);
}

这一次,没有 Task.Run 或任何其他线程池线程的显式使用。

有关更多详细信息,请参阅我的 brownfield async article 的“转换”部分.

关于c# - 如何将 async/await 与返回 ObservableCollection<T> 的方法一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39173572/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com