代码之家  ›  专栏  ›  技术社区  ›  Mark Carpenter

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

  •  9
  • Mark Carpenter  · 技术社区  · 14 年前

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

    目前,为了支持取消WCF请求,我使用由svcutil生成的BeginMyFunction/EndMyFunction方法(并在将结果发送到主线程时处理isCanceled标志)。对于异步调用,我想使用myfunctionsync方法(而不是挂接到myfunctionsynccompleted事件),而不是Begin/End。

    谢谢!

    我已经决定要在每次调用的基础上创建WcfClient对象(而不是每个WPF页或每个应用程序),所以我想到了以下几点:

    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 回复  |  直到 14 年前
        1
  •  7
  •   bju1046    14 年前

    除非手动创建异步函数,否则无法取消异步请求。考虑到您正在自动生成WCF调用,这将使它更像是一件家务事。即使如此,就像你说的,电话不会取消,它仍会继续运行。如果仍要取消,只需确保客户机/UI忽略调用的结果。

        2
  •  20
  •   McGuireV10    6 年前

    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.
        }
    }
    
        3
  •  4
  •   user3285954    10 年前

    .Net 4.5版

    1. CancellationTokenSource.Token传递给WCF方法启动的所有方法调用,包括数据库调用和网络调用(如果适用)。
    2. WCF方法可以将唯一标识符作为参数,也可以返回与CancellationTokenSource关联的唯一标识符。
    3. 当客户端需要取消操作时,调用一个WCF方法并传入上一个调用的唯一标识符。
    4. CancellationTokenSource使用唯一标识符检索,并调用其Cancel方法。如果取消令牌被正确处理,由上一个调用启动的操作将很快被取消,并且可以返回OperationCanceledException或CancelFault或其他一些错误,这些错误指示客户端该调用被取消。
    5. 当客户端在第4步同时调用cancel时,可以中止原来的WCF调用。但是,如果客户端正确处理OperationCanceledException或CancelFault,则不需要这样做。如果最初的调用是从网页启动的,OperationCanceledException甚至可以冒泡到ajax调用。

    在CancellationToken还不可用的旧的.Net框架中也可以实现类似的功能。

        4
  •  2
  •   Greg D    14 年前

    取消一个请求在一般意义上是不实际的,因为你所说的请求是异步的。可以执行特定的解决方法。例如,向您的协议添加一个新的Cancel信号,该信号设置一些共享状态,您的主要、长时间运行的任务会定期检查这些状态,以验证它是否应该继续执行。

    无论哪种方式,由于请求的异步性质,我认为客户机仍有责任知道忽略已取消请求的结果。无法保证客户端不会收到来自请求的响应,因为客户端和服务器之间存在竞争,请求被取消。可以添加一个额外的层来监视某个内容是否被取消,并知道如何丢弃请求的响应,但这并不能阻止结果被传输到客户端。

        5
  •  1
  •   BasvdL    14 年前

    MyFunctionCompleted 事件,因为它是在UI线程中处理的。

    Abort 上的方法 WcfClient (您需要保留引用)。这将清理客户端的资源。服务器仍将完成请求,但客户端将不再等待它。不久之后 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();
            }
        }
    }