views:

241

answers:

3

(Assuming a WCF method called "MyFunction")

Currently, to support canceling a WCF request, I'm using the BeginMyFunction/EndMyFunction methods generated by svcutil (and handling an isCanceled flag when dispatching the results to the main thread). I'd like to use the MyFunctionAsync method (and hooking into MyFunctionAsyncCompleted event instead) for async calls instead of Begin/End.

What is the best/supported way to handle canceling WCF requests if using MyFunctionAsyncCompleted, and still ensuring that the event doesn't get fired on a page that's no longer loaded (i.e. page navigation within a frame).

Thanks!

EDIT:

I've decided that I want to create my WcfClient object on a per-call basis (as opposed to per-WPF-Page or per-Application), so here's what I've come up with:

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

I'm finding it difficult to figure out what the recommended way to accomplish what I've just implemented is. Is this fine as-is, or are there pitfalls/edge cases that I need to worry about? What's the golden standard when it comes to properly canceling a WCF call?

+1  A: 

Cancelling a request isn't really practical in the general sense simply due to the asynchronous nature of the request you're talking about. Specific workarounds can be performed. E.g., adding a new Cancel signal to your protocol that sets some shared state that your primary, long-running task periodically checks to verify that it should continue executing.

Either way, once again due to the asynchronous nature of the request, I believe that it's still the client's responsibility to know to ignore results from a request that has been cancelled. It isn't possible to guarantee that the client won't receive a response from a request that it's just cancelled simply because a race exists between the client and the server. It's possible to add an additional layer that monitors whether something has been cancelled and knows to throw away a request's response, but that's not preventing the result from being transmitted to the client.

Greg D
+1  A: 

There is no way to cancel the asynchronous request unless you manually create the asynchronous functions. Considering that you are auto generating your WCF calls that would make it more of a chore. Even then like you said the call does not cancel it will still run it's course. If you still want to have a cancel you just have to make sure the client/UI ignores the results from the call.

bju1046
A: 

If I understand you correctly you are trying to correctly abort a pending call to a WCF service. You want to use the MyFunctionCompleted event because it is handled in the UI Thread.

What you probably should do is call the Abort method on the WcfClient (you need to keep a reference to it). This will cleanup resources on the client side. The server will stil finish the request, but the client will not wait on it any more. Shortly after the MyFunctionCompleted event is triggered. By checking the client.State you will know whether the call succeeded, failed or was aborted.

Here is a small test app having a Send button, a Abort button and a textbox for the results:

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

There is no exception handling and cleaning up... I don't know if it is the recommended way, but I think calling abort is the right way. You need to handle the different error situations any way.

BasvdL