4

I have a WPF application where one of the properties in the View Model will be populated as a call to a service. I have been following Stephen Cleary's excellent tutorials so far. He discusses one way of doing this here

In my case the view model is instantiated once per application. The application calls the Initialise method (custom method) on the view model passing in some information on the basis of which the View model is supposed to reach out to the service to get the property instantiated.

The problem is that the application can call the Initialise method multiple times (user moving around randomly) passing in new set of information. When this happens I need to discard the earlier running tasks (if any) which were called the previous time the Initialise was called, call the service with fresh set of information and ensure that the property is only bound to the result of the latest invocation.

Can someone help come up with a pattern to achieve this? Basically call an async method multiple times but keep only the last result.

2
  • Will you be calling them multiple times concurrently? Or will your application call the Initialize method a couple of times but with a diff between the calls? Commented Sep 24, 2014 at 12:50
  • The initialise method is called when user clicks a button so the calls will not be concurrent but sequential. Commented Sep 24, 2014 at 12:53

1 Answer 1

2

Basically, you want to cancel the previous invocation of the Initialize method. And in TPL, if you want to cancel something, you should usually use CancellationToken.

The way you could do that here is to have a field of type CancellationTokenSource in your view model, representing cancellation of the last invocation of Initialize. When you run Initialize, it cancels the previous invocation, sets up its own cancellation, calls the service and then only sets the property if cancellation wasn't requested. In code:

class ViewModel
{
    // default value just to avoid a null check
    private CancellationTokenSource intializationCancellation =
        new CancellationTokenSource();

    public async Task InitializeAsync(int parameter)
    {
        // cancel previous initialization, if any
        intializationCancellation.Cancel();

        var cts = new CancellationTokenSource();
        intializationCancellation = cts;

        var value = await GetValueaAsync(parameter);

        if (cts.Token.IsCancellationRequested)
            return;

        Value = value;
    }

    private async Task<string> GetValueAsync(int parameter)
    {
        // call the external service here
    }

    public string Value { get; private set; }
}

If the service you're calling supports cancellation, you should pass the CancellationToken to it too, which is likely to save some resources. If you do that, don't forget to catch the resulting OperationCanceledException (since I believe you don't want Initialize to throw, even if it's canceled).

Sign up to request clarification or add additional context in comments.

1 Comment

Your answer is text book correct but is not really applicable to my problem. Given the high level of COM Interop that I was also having to deal with there was no straight pattern that would help resolve this problem. Things were also complicated as I need to ensure that the threads running tasks were STA which TPL does not guarantee. All in all I had to re-engineer a fair bit but I have things working as I wanted now. Thank you anyways!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.