Reputation: 25414
This question extends my previous one Asynchronuos binding and LINQ query hangs. Assume I have a LINQ such us:
var query = from var item in items where item.X == 1 select item;
I can iterate throughout the query
asynchronuosly and dispatch each item to UI (or I may use IProgress
):
foreach(var item in query)
{
Application.Current.Dispatcher.BeginInvoke(
new Action(() => source.Add(item)));
}
Now I would like to cancel the query... I can simply declare a CancellactionTokenSource
cts, put a token into a task and then:
foreach(var item in query)
{
cts.Token.ThrowIfCancellationRequested();
Application.Current.Dispatcher.BeginInvoke(
new Action(() => source.Add(item)));
}
The trouble is, that I'm able to cancel only when new result appears. So if there is a long chain of items, that don't meet my query condition, my cancel request is ignored.
How to involve cancellation into LINQ (to objects) and be able to check the cancel token for each item?
Upvotes: 1
Views: 2158
Reputation: 246
For my specific to Entity Framework version of this problem:
I've been digging around for a while today trying to find an answer for this when I finally found something (one of the 30ish pages I visited) that was a clear answer (for my) issue which is specifically a linq query running against entity framework.
for later versions of Entity Framework (as of now) there are extension methods for ToListAsync which include an overload that take a cancellation token.
as does task (in my case my query was in a task) I ran the query in, but it was the data query I was most concerned about.
var sourceToken = new System.Threading.CancellationTokenSource();
var t2 = System.Threading.Tasks.Task.Run(() =>
{
var token = sourceToken.Token;
return context.myTable.Where(s => s.Price == "Right").Select(i => i.ItemName).ToListAsync(token);
}
, sourceToken.Token
);
Upvotes: 0
Reputation: 4526
I'm not sure as I didn't test it... But I think you could put it as side-effect inside your linq query, maybe creating a method inside your where to do so, such as:
Change this:
var query = from var item in items where item.X == 1 select item;
To:
var query = from var item in items where CancelIfRequestedPredicate(item,i=>i.X == 1) select item;
And create a method:
private bool CancelIfRequestedPredicate<T>(T item,Predicate<T> predicate)
{
cts.Token.ThrowIfCancellationRequested();
return predicate(item);
}
Since linq uses deferred execution, I think it will run your method at each iteration.
Just as an observation, I don't know what will be the behavior if you're not using Linq to Objects (You didn't mention if you're using linq to sql, or something like this). But it probably won't work.
Hope it helps.
Upvotes: 2