Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the best way to cancel an asynchronous WCF request?

(Assuming a WCF method called "MyFunction")

Currently, to support canceling a WCF request, I'm using the BeginMyFunction/EndMyFunction methods generated by svcutil (and handling an isCanceled flag when dispatching the results to the main thread). I'd like to use the MyFunctionAsync method (and hooking into MyFunctionAsyncCompleted event instead) for async calls instead of Begin/End.

What is the best/supported way to handle canceling WCF requests if using MyFunctionAsyncCompleted, and still ensuring that the event doesn't get fired on a page that's no longer loaded (i.e. page navigation within a frame).

Thanks!

EDIT:

I've decided that I want to create my WcfClient object on a per-call basis (as opposed to per-WPF-Page or per-Application), so here's what I've come up with:

public void StartValidation(){
    WcfClient wcf = new WcfClient();
    wcf.IsValidCompleted += new EventHandler<IsValidCompletedEventArgs>(wcf_IsValidCompleted);
    //pass the WcfClient object as the userState parameter so it can be closed later
    wcf.IsValidAsync(TextBox.Text, wcf);  
}

void wcf_IsValidCompleted(object sender, IsValidCompletedEventArgs e) {
    if(!m_IsCanceled){
        //Update the UI
        //m_IsCanceled is set to true when the page unload event is fired
    }
    //Close the connection
    if (e.UserState is WcfClient) {
        ((WcfClient)e.UserState).Close();
    }
}

I'm finding it difficult to figure out what the recommended way to accomplish what I've just implemented is. Is this fine as-is, or are there pitfalls/edge cases that I need to worry about? What's the golden standard when it comes to properly canceling a WCF call?

like image 943
Mark Carpenter Avatar asked Jun 14 '10 17:06

Mark Carpenter


2 Answers

Easiest way I know of to do this with a WCF Client and the Task based async pattern is to register an Abort action with the cancellation token

private async Task CallOrAbortMyServiceAsync(CancellationToken cancellation)
{
    var client = new SomeServiceClient();
    await using var registration = cancellation.Register(client.Abort);
    try
    {
         await client.CallMyServiceAsync();
    } catch (CommunicationObjectAbortedException) {
          // This will be called when you are cancelled, or some other fault.
    }
}
like image 157
Flatliner DOA Avatar answered Oct 25 '22 23:10

Flatliner DOA


.Net 4.5

  1. Every WCF call implementation creates a CancellationTokenSource and stores it in a location accessible to all WCF services. I.e. in single server environments can be in an in memory cache.
  2. CancellationTokenSource.Token is passed to all method calls initiated by WCF method, including calls to databases and network calls where applicable.
  3. The WCF method can have a unique identifier as parameter or can return a unique identifier what is associated with CancellationTokenSource.
  4. When client needs to cancel the operation calls a WCF method and passes in the unique identifier of previous call.
  5. CancellationTokenSource is retrieved using the unique identifier and its Cancel method is invoked. If cancellation token is properly handled the operation started by previous call will soon be cancelled and can either return OperationCanceledException or a CancelFault or some other fault what signals client that call was cancelled.
  6. When client calls cancel at step 4 at the same time can abort the original WCF call. However if clients properly handle OperationCanceledException or CancelFault this isn't needed. OperationCanceledException can even bubble up to an ajax call if original call was started from a web page.

Something similar can be implemented in older .Net frameworks too where CancellationToken is not yet available.

like image 34
user3285954 Avatar answered Oct 26 '22 00:10

user3285954