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?
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
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);
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