简体   繁体   English

如何取消异步WCF调用?

[英]How to cancel an async WCF call?

I have been trying some time to find out how to implement WCF call cancellation based on the new .NET 4.5 CancellationToken mechanism. 我一直在尝试一些时间,以了解如何基于新的.NET 4.5 CancellationToken机制实现WCF呼叫取消。 All the samples I found are not WCF based, and do not cross the service boundary. 我发现的所有样本都不基于WCF,并且没有跨越服务边界。

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+ 请提供适用于.NET 4.5+上WCF的具体示例

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. 因此,想象一下,我将服务设置为ContextMode=Session ,然后设置一个静态变量(在单独的服务调用中)称为cancellationPending=true ,我的原始调用在此阶段仍在运行,并且会定期检查该变量。 Would I still not be able to cancel? 我仍然无法取消吗? would it still be impossible? 还是不可能吗? if so, why? 如果是这样,为什么?

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 如果要在客户端取消任务,则可以使用Microsoft.VisualStudio.Threading.ThreadingTools的扩展方法WithCancellation

It is part of Visual Studio SDK or you can also get it from Nuget . 它是Visual Studio SDK的一部分,或者您也可以从Nuget获取它。

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

In two words - this is impossible. 用两个词来说-这是不可能的。

If you understand what WCF call is by its nature, it becomes obvious. 如果您了解WCF调用的本质,那么它将变得显而易见。

Looking on your example, what we see generally: Two separate machine A and B, connected via networks. 看您的示例,我们通常会看到:通过网络连接的两台单独的机器A和B。 On machine A you have some component which listen a socket, and when it gets a command it starts synchronous long computation RemoteCall() . 在机器A上,您有一些侦听套接字的组件,当它收到命令时,它将启动同步长计算RemoteCall()

On machine B you have a client which has two autogenerated methods in fact 在计算机B上,您有一个实际上具有两个自动生成方法的客户端

BeginRemoteCall->IAsyncResult + EndRemoteCall(IAsyncResult) . 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 本质上,新的基于任务的异步调用版本(aka RemoteCallAsync()->Task )完全相同,并且您始终可以使用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. 当您在客户端上调用BeginRemoteCall时,将命令发送到第二台计算机“请开始计算”,然后添加一个回调,在完成计算并得出结果时将调用该回调。 Internally this async implementation uses I/O completion ports and allows your client to be non-blocking. 在内部,此异步实现使用I / O完成端口,并允许您的客户端处于非阻塞状态。

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 而已

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. 1)使用InstanceContextMode=InstanceContextMode.PerSession创建服务,这样可以保证您具有用于服务后续请求的相同服务实例。

2) to start new sessions use service operations marked with OperationContract(IsInitiating = True) 2)要启动新会话,请使用标有OperationContract(IsInitiating = True)服务操作

3) create as service member. 3)创建为服务成员。 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. 4)在每种服务方法内部,您将必须在任务内部开始新的计算并将取消令牌作为此任务的参数。

[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. 这是 WCF会话的快速描述

All the examples are just from my head, haven't tried if it really work. 所有示例均来自我的脑海,尚未尝试过是否真的有效。 But you can try. 但是您可以尝试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM