Ryszard Dżegan
Ryszard Dżegan

Reputation: 25414

How to cancel asynchronuos action in LINQ query?

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

Answers (2)

wode
wode

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

Conrad Clark
Conrad Clark

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

Related Questions