How to cancel an async WCF call?

怎甘沉沦 提交于 2019-11-28 05:49:12

问题


I have been trying some time to find out how to implement WCF call cancellation based on the new .NET 4.5 CancellationToken mechanism. All the samples I found are not WCF based, and do not cross the service boundary.

My service :

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class MyService : IMyService
{   
    public void LongOperation()
    {
        // do stuff that takes ages...
        // please cancel me!
    }
}

My Client (Desktop App):

Using an auto generated proxy :

private async void DoLongRunningTaskAsync()
{
    var asyncTask = _myService.LongOperationAsync();
    await asyncTask;
}

How do I cancel this task? Please provide a concrete example applicable to WCF on .NET 4.5+

EDIT:

The answers below seem to indicate that its impossible for technical reasons. So, imagine, I make my service ContextMode=Session, then set a static variable (in a separate service call) called cancellationPending=true, my original call is still running at this stage, and it periodically checks that variable. Would I still not be able to cancel? would it still be impossible? if so, why?


回答1:


In two words - this is impossible.

If you understand what WCF call is by its nature, it becomes obvious.

Looking on your example, what we see generally: Two separate machine A and B, connected via networks. On machine A you have some component which listen a socket, and when it gets a command it starts synchronous long computation RemoteCall().

On machine B you have a client which has two autogenerated methods in fact

BeginRemoteCall->IAsyncResult + EndRemoteCall(IAsyncResult).

New Task based version of asynchronous call (aka RemoteCallAsync()->Task) by the nature is absolutely the same, and you can always create your own task using TaskFactory.FromAsync(BeginRemoteCall,EndRemoteCall)->Task

When you call BeginRemoteCall on the client, you send the command to the second machine "please start computation", and then you add a callback, which will be called, when calculation is done and results came. Internally this async implementation uses I/O completion ports and allows your client to be non-blocking.

But there is no way to cancel an operation on the server side, if it is already started. You may not be interested in results, you may ignore your callback, but the computation on the server side will be finished anyway.

That's it




回答2:


As indicated before. It is impossible to cross service boundary and cancel on server side.

If you want to cancel the Task on client side you can can use the extension method WithCancellation from Microsoft.VisualStudio.Threading.ThreadingTools

It is part of Visual Studio SDK or you can also get it from Nuget.

 CancellationTokenSource ct = new CancellationTokenSource();
 ct.CancelAfter(20000);
 var asyncTask = _myService.LongOperationAsync();
 await asyncTask.WithCancellation(ct.Token);



回答3:


After the edit of the question I see that it is acceptable to have cancellation of logic level. If so, the following algorithm can be tried.

1) Create a service with InstanceContextMode=InstanceContextMode.PerSession, so that will guarantee you having the same instance of service for serving subsequent requests.

2) to start new sessions use service operations marked with OperationContract(IsInitiating = True)

3) create as service member. not static. it will be service state

CancellationTokenSource ct = new CancellationTokenSource();

4) inside every service method you will have to start new computations inside tasks and put cancellation token as a parameter for this task.

[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface ICalculator
{
   [OperationContract(IsInitiating = True)]
   Bool Begin()

   [OperationContract(IsInitiating = false)]
   double Add(double n1, double n2)
}

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class CalculatorService : ICalculator
{
    CancellationTokenSource cts = new CancellationTokenSource();

    public double Add(double n1, double n2)
    {
        var cancellationToken = cts.Token;

        var t = Task<double>.Factory.StartNew(() => { 
           // here you can do something long
           while(true){
           // and periodically check if task should be cancelled or not
           if(cancellationToken.IsCancellationRequested)
             throw new OperationCanceledException();

           // or same in different words
           cancellationToken.ThrowIfCancellationRequested();
           }
           return n1 + n2;
        }, cancellationToken);

        return t.Result;
    }

    public Bool Begin(){}
}

Here is a quick descriptions of WCF sessions.

All the examples are just from my head, haven't tried if it really work. But you can try.



来源:https://stackoverflow.com/questions/33395234/how-to-cancel-an-async-wcf-call

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!