Asynchronuos and LINQ Interaction

I have a UI control that accepts Enumerable as a binding source. But until I set the binding source, I have to filter out the original Enumerable . I would like to use LINQ for this purpose:

 control.BindingSource = from var item in enumerable.Cast<ItemType>() where item.X == 1 select item; 

This is a user interface dependent problem, since an enumerated one is slow (for example, if it is implemented as yield return new Item(); Thread.Sleep(1000) ... ) and tries to control the execution of the request in the user interface thread. I tried to solve this problem using a combination of Task and async-wait:

 control.BindingSource = await Task.Factory.StartNew(() => (from var item in enumerable.Cast<ItemType>() where item.X == 1 select item).ToArray()); 

Now the user interface does not freeze, but the results are displayed immediately after completion of the query. I solve this using ObservableCollection and Enumerator with await next to MoveNext in while :

 var source = new ObservableCollection<object>(); control.BindingSource = source; var enumerator = enumerable.GetEnumerator(); while (await Task.Factory.StartNew(() => enumerator.MoveNext())) { var item = (ItemType)enumerator.Current; if (item.X == 1) source.Add(item); } 

I am looking for a solution that will allow at least LINQ. Any ideas?

+4
source share
2 answers

Unfortunately, async does not work very well with LINQ. There is an “asynchronous enumerator” that the Rx team experimented with, but I believe that it was abandoned.

A task-based asynchronous template has a standard approach for progress reporting, which you can use here.

 private static void EvaluateItems(IEnumerable<ItemType> items, IProgress<ItemType> progress) { if (progress == null) return; var query = from var item in items where item.X == 1 select item; foreach (var result in query) progress.Report(result); } var source = new ObservableCollection<object>(); control.BindingSource = source; var progress = new Progress<ItemType>(item => source.Add(item)); await Task.Run(() => EvaluateItems(enumerable.Cast<ItemType>(), progress); 

IProgress - IProgress -based code has a greater separation of concerns. The EvaluateItems method is intended only for listing and filtering elements. He should not know that they are part of the ObservableCollection or that he is working in a WPF ( Dispatcher ) application. Thus, it is more portable and verifiable.

+2
source

Try it like this:

 var query = from var item in enumerable.Cast<ItemType>() where item.X == 1 select item; var source = new ObservableCollection<object>(); control.BindingSource = source; Task.Factory.StartNew( () => { foreach(var item in query) { Application.Current.Dispatcher.BeginInvoke( new Action(() => source.Add(item))); } }, TaskCreationOptions.LongRunning); 
+1
source

Source: https://habr.com/ru/post/1494472/


All Articles