取消异步WCF请求的最佳方法是什么?

时间:2010-06-14 17:17:45

标签: wpf wcf asynchronous

(假设一个名为“MyFunction”的WCF方法)

目前,为了支持取消WCF请求,我使用svcutil生成的BeginMyFunction / EndMyFunction方法(并在将结果分派给主线程时处理isCanceled标志)。我想使用MyFunctionAsync方法(并挂钩到MyFunctionAsyncCompleted事件)来进行异步调用而不是Begin / End。

如果使用MyFunctionAsyncCompleted,处理取消WCF请求的最佳/支持方式是什么,并且仍然确保不会在不再加载的页面上触发事件(即帧内的页面导航)。

谢谢!

编辑:

我已经决定要在每次调用的基础上创建我的WcfClient对象(而不是每个WPF-Page或per-Application),所以这就是我提出的:

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();
    }
}

我发现很难弄清楚我刚才实现的建议方法是什么。这是好的,还是我需要担心的陷阱/边缘情况?正确取消WCF呼叫的黄金标准是什么?

5 个答案:

答案 0 :(得分:15)

我知道使用WCF客户端执行此操作的最简单方法,基于任务的异步模式是使用取消令牌注册中止操作

private async Task CallOrAbortMyServiceAsync(CancellationToken cancellation)
{
    var client = new SomeServiceClient();
    cancellation.Register(() => client.Abort());
    try
    {
         await client.CallMyServiceAsync();
    } catch (CommunicationObjectAbortedException) {
          // This will be called when you are cancelled, or some other fault.
    }
}

答案 1 :(得分:7)

除非您手动创建异步函数,否则无法取消异步请求。考虑到你是自动生成你的WCF调用,这将使它更多的苦差事。即便如此,就像你说电话没有取消它仍然会运行它的过程。如果你仍然希望取消,你只需要确保客户端/ UI忽略来自呼叫的结果。

答案 2 :(得分:3)

.Net 4.5

  1. 每个WCF调用实现都会创建一个CancellationTokenSource,并将其存储在所有WCF服务都可访问的位置。即在单服务器环境中,可以在内存缓存中。
  2. CancellationTokenSource.Token传递给WCF方法发起的所有方法调用,包括在适用的情况下调用数据库和网络调用。
  3. WCF方法可以将唯一标识符作为参数,也可以返回与CancellationTokenSource关联的唯一标识符。
  4. 当客户端需要取消操作时,调用WCF方法并传入先前调用的唯一标识符。
  5. 使用唯一标识符检索CancellationTokenSource,并调用其Cancel方法。如果正确处理了取消令牌,则前一次调用启动的操作将很快被取消,并且可以返回OperationCanceledException或CancelFault或其他一些故障,这些错误表示客户端该呼叫已被取消。
  6. 当客户端呼叫在步骤4同时取消时可以中止原始WCF呼叫。但是,如果客户端正确处理OperationCanceledException或CancelFault,则不需要。如果从网页启动原始调用,OperationCanceledException甚至可以冒泡到ajax调用。
  7. 类似的东西也可以在旧的.Net框架中实现,而CancellationToken尚不可用。

答案 3 :(得分:2)

由于您正在谈论的请求的异步性质,取消请求在一般意义上并不实际。可以执行特定的解决方法。例如,在您的协议中添加一个新的取消信号,该信号设置了一个长期运行的主要任务定期检查以确认它应该继续执行的共享状态。

无论哪种方式,再次由于请求的异步性质,我相信客户有责任知道忽略已被取消的请求的结果。由于客户端和服务器之间存在竞争,因此无法保证客户端不会从刚刚取消的请求中收到响应。可以添加一个额外的层来监视某些内容是否已被取消并知道丢弃请求的响应,但这并不妨碍将结果传输到客户端。

答案 4 :(得分:1)

如果我理解正确,您正在尝试正确中止对WCF服务的待处理调用。您想使用MyFunctionCompleted事件,因为它是在UI线程中处理的。

您应该做的是调用Abort上的WcfClient方法(您需要保留对它的引用)。这将清理客户端上的资源。服务器将完成请求,但客户端将不再等待它。触发MyFunctionCompleted事件后不久。通过检查client.State,您将知道呼叫是成功,失败还是已中止。

这是一个小型测试应用,具有发送按钮,中止按钮和结果文本框:

public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();
    }

    private SomeServiceClient m_client;

    private void buttonSend_Click(object sender, EventArgs e)
    {
        m_client = new SomeServiceClient();
        m_client.MyFunctionCompleted += new EventHandler<MyFunctionCompletedEventArgs>(client_MyFunctionCompleted);
        m_client.MyFunctionAsync(4000, m_client);
    }

    private void buttonAbout_Click(object sender, EventArgs e)
    {
        if( m_client != null ) 
            m_client.Abort();
    }

    void client_MyFunctionCompleted(object sender, MyFunctionCompletedEventArgs e)
    {

        var client = e.UserState as SomeServiceClient;
        if (client != null)
        {
            if (client.State == System.ServiceModel.CommunicationState.Opened)
            {
                textBox.Text += string.Format("Result: {0}\r\n", e.Result);
                client.Close();
                return;
            }
            else if (client.State == System.ServiceModel.CommunicationState.Faulted)
            {
                textBox.Text += string.Format("Error: {0}\r\n", e.Error.Message);
            }
            client.Abort();
        }
    }
}

没有异常处理和清理......我不知道这是否是推荐方式,但我认为调用abort是正确的方法。您需要以任何方式处理不同的错误情况。