Каков наилучший способ отменить асинхронный запрос WCF? - PullRequest
9 голосов
/ 14 июня 2010

(при условии, что метод WCF называется «MyFunction»)

В настоящее время для поддержки отмены запроса WCF я использую методы BeginMyFunction / EndMyFunction, сгенерированные svcutil (и обрабатываю флаг isCanceled при отправке результатовв основной поток).Я хотел бы использовать метод MyFunctionAsync (и вместо этого подключиться к событию MyFunctionAsyncCompleted) для асинхронных вызовов вместо Begin / End.

Каков наилучший / поддерживаемый способ обработки отмены запросов WCF, если используется MyFunctionAsyncCompleted, и при этом гарантируется, что событие не будет запущено на странице, которая больше не загружается (т. Е. Навигация по страницам во фрейме).

Спасибо!

РЕДАКТИРОВАТЬ:

Я решил, что хочу создать свой объект WcfClient для каждого вызова (в отличие отper-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 ]

15 голосов
/ 28 июня 2016

Самый простой способ сделать это с клиентом 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.
    }
}
7 голосов
/ 24 июня 2010

Невозможно отменить асинхронный запрос, если вы не создадите асинхронные функции вручную.Учитывая, что вы автоматически генерируете свои вызовы WCF, это сделает его более рутинным делом.Даже тогда, как вы сказали, звонок не отменяется, он все равно будет работать.Если вы все еще хотите отменить запрос, просто убедитесь, что клиент / пользовательский интерфейс игнорирует результаты вызова.

3 голосов
/ 13 декабря 2014

.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-вызов, если исходный вызов был запущен с веб-страницы.

Нечто подобное можно реализовать и в старых платформах .Net, где CancellationToken еще не доступен.

2 голосов
/ 24 июня 2010

Отмена запроса не очень практична в общем смысле просто из-за асинхронного характера запроса, о котором вы говорите.Конкретные обходные пути могут быть выполнены.Например, добавление нового сигнала отмены в ваш протокол, который устанавливает некоторое общее состояние, которое ваша первичная долгосрочная задача периодически проверяет, чтобы убедиться, что оно должно продолжать выполняться.

В любом случае, еще раз из-за асинхронной природызапрос, я считаю, что ответственность за игнорирование результатов запроса, который был отменен, по-прежнему лежит на клиенте.Невозможно гарантировать, что клиент не получит ответ на запрос, который был просто отменен просто потому, что существует конфликт между клиентом и сервером.Можно добавить дополнительный слой, который отслеживает, было ли что-то отменено, и знает, что отбрасывает ответ на запрос, но это не мешает передаче результата клиенту.

1 голос
/ 25 июня 2010

Если я вас правильно понимаю, вы пытаетесь правильно прервать ожидающий вызов службы WCF. Вы хотите использовать событие MyFunctionCompleted, потому что оно обрабатывается в потоке пользовательского интерфейса.

Что вам, вероятно, следует сделать, это вызвать метод 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 - это правильный путь. Вам нужно как-то разбираться с различными ошибочными ситуациями.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...