C # асинхронный обратный вызов на удаленной форме - PullRequest
2 голосов
/ 14 июня 2010

Быстрый вопрос: одна из моих форм в моем приложении winform (c #) выполняет асинхронный вызов службы WCF для получения некоторых данных. Если форма закрывается до выполнения обратного вызова, происходит сбой с ошибкой при доступе к удаленному объекту. Как правильно проверить / справиться с этой ситуацией? Ошибка возникает при вызове метода Invoke для обновления моей формы, но я не могу перейти к внутреннему исключению, потому что оно говорит, что код был оптимизирован.

Код:

  public void RequestUserPhoto(int userID)
        {
            WCF.Service.BeginGetUserPhoto(userID,
                new AsyncCallback(GetUserPhotoCB), userID);
        }</p>

<pre><code>    public void GetUserPhotoCB(IAsyncResult result)
    {
        var photo = WCF.Service.EndGetUserPhoto(result);
        int userID = (int)result.AsyncState;
        UpdateUserPhoto(userID, photo);
    }

    public delegate void UpdateUserPhotoDelegate(int userID, Binary photo);
    public void UpdateUserPhoto(int userID, Binary photo)
    {
        if (InvokeRequired)
        {
            var d = new UpdateUserPhotoDelegate(UpdateUserPhoto);
            Invoke(d, new object[] { userID, photo });
        }
        else
        {
            if (photo != null)
            {
                var ms = new MemoryStream(photo.ToArray());
                var bmp = new System.Drawing.Bitmap(ms);
                if (userID == theForm.AuthUserID)
                {
                    pbMyPhoto.BackgroundImage = bmp;
                }
                else
                {
                    pbPhoto.BackgroundImage = bmp;
                }
            }
        }
    }
</code>

UPDATE:

Я до сих пор не знаю, куда идти с этим. Что мне действительно нужно, так это шаблон проектирования для выполнения вызовов асинхронных служб WCF из выигрышной формы, который элегантен в обработке форм, закрывающихся до того, как асинхронный вызов может вернуться. Пользователь может щелкнуть значок X в форме или в любой форме в любое время. Проблема намного больше, чем один пример, который я показал выше. Мое приложение на самом деле делает сотни вызовов WCF, и я пытаюсь понять, как правильно и согласованно обрабатывать их во всем приложении. Например, если мне нужно добавить 100 строк кодов с ManualResetEvents или фоновыми рабочими, или мьютексами, или чем-то еще, для каждого вызова WCF просто так, чтобы это не бомбило мое приложение, это создаст много места для ошибки. Что мне нужно, так это чистый способ вызвать службу асинхронно, а затем преобразовать ее в односторонний вызов, если форма закрывается. Другими словами, пусть служба завершит работу, но мне все равно, каковы результаты, и не вызывайте обратный вызов, потому что его больше нет.

Ответы [ 5 ]

1 голос
/ 15 июня 2010
public void UpdateUserPhoto(int userID, Binary photo)
{
  if ( Disposed || !IsHandleCreated )
    return;
  if (InvokeRequired)
  ...
0 голосов
/ 22 июля 2010

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

Автоматически созданный прокси WCF создает методы вызова синхронизации, асинхронные методы с использованием шаблона начала / конца и делегаты на основе события с использованием события Completed.В примере, который я разместил выше в своем первоначальном вопросе, использовался шаблон «Начало / Конец».Проблема состоит в том, что когда обратный вызов сделан, вам нужно будет выполнить Invoke для доступа к вашему потоку пользовательского интерфейса.Если этот поток пользовательского интерфейса больше не существует (т.е. пользователь закрыл окно), у вас есть проблемы.Новый шаблон, основанный на событиях, автоматически находит свой путь назад к потоку пользовательского интерфейса, и из моего тестирования я не смог заставить его завершиться сбоем, закрывшись до завершения службы.Я предполагаю, что прокси достаточно умен, чтобы НЕ вызывать завершенный обработчик, если адрес памяти не существует?Или, может быть, он зависает от адреса памяти обработчику, поэтому он не будет собирать мусор?Поэтому все, что вам нужно сделать, это добавить обработчик события Completed, завершить вызов и т. Д., ServicenameAsync () и дождаться его возврата в ваш обработчик (в потоке пользовательского интерфейса).Я также, просто чтобы убедиться, обернул свои законченные обработчики в блоки try / catch для обработки ObjectDisposedExceptions, если таковые имеются.

Главное сейчас: ЭТО НЕ РАБОТАЕТ.

Одна ошибка (по крайней мере, для меня) ... Я использовал шаблон синглтона для доступа к моей службе WCF.Проблема в том, что он создает статическую ссылку на ваш прокси WCF, используемую во всем приложении.Звучит удобно, но когда вы добавляете обработчики событий к событиям Completed непосредственно перед тем, как выполнить асинхронный вызов, они могут дублироваться, дублироваться и т. Д. Каждый раз, когда вы делаете вызов, вы добавляете ДРУГОЙ обработчик завершенных событий в дополнение к одному.уже добавлен в ваш статический прокси WCF.Чтобы решить эту проблему, я начал объявлять новых прокси-клиентов для каждого вызова или, если для каждого класса winform не выполняется несколько вызовов, для каждой формы win.Если у кого-то есть комментарии по этому или лучшему способу, ПОЖАЛУЙСТА, дайте мне знать!Самая большая проблема с одноэлементным шаблоном заключается в том, что если у вас открыто несколько окон (разных классов), которые вызывают один и тот же асинхронный метод, но вы хотите, чтобы они возвращались к различным обработчикам Completed, вы не сможете этого сделать.

0 голосов
/ 15 июня 2010

Создайте флаг, что-то вроде «IsWaiting» в качестве ManualResetEvent (или даже простого bool), установите его в true и устанавливайте в false только тогда, когда ваш асинхронный результат вернется.

В вашем классе dispose метод ставит проверку на флаг и удаляет объект только после того, как флаг очищен. (Укажите тайм-аут на случай, если возникнет ошибка)

0 голосов
/ 15 июня 2010

Я считаю, что в объекте Form есть свойство IsDisposed. Вы можете проверить это свойство перед вызовом Invoke.

0 голосов
/ 14 июня 2010

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

Я бы исследовал BackgroundWorker класс . Я уверен, что вы по-прежнему можете настроить аварийное завершение, закрыв форму до срабатывания обратного вызова, но с BackgroundWorker вы сможете опрашивать его статус и обрабатывать вашу ситуацию более изящно.

Вы не должны закрывать форму, пока асинхронный вызов еще активен. Используя BackgroundWorker, вы можете легко определить, активен ли асинхронный вызов.

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