Асинхронные службы WCF в WPF - события - PullRequest
3 голосов
/ 28 сентября 2011

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

Итак, код модели вида выглядит так:

WebServiceAgent.GetProductByID(SelectedProductID, (s, e)=>{States = e.Result;});

А агент WebService выглядит так:

    public static void GetProductByID(int ProductID, EventHandler<GetProductListCompletedEventArgs> callback)
    {
        Client.GetProductByIDCompleted += callback;
        Client.GetProductByIDAsync(ProductID);
    }

Это хороший подход? Я использую легкий инструментарий MVVM. Таким образом, модель представления статична, поэтому в течение всего жизненного цикла приложения модель представления остается. Но каждый раз, когда модель представления вызывает этот WebServiceAgent, я думаю, что регистрирую событие. Но это событие не является незарегистрированным.

Это проблема? Допустим, модель просмотра вызывается 20 - 30 раз. Я вставляю какую-то утечку памяти?

Ответы [ 4 ]

7 голосов
/ 07 октября 2011

Некоторая полезная информация, основанная на ошибках, которые я узнал от себя:

  • Кажется, что объект Client используется снова и снова. Если не отменять регистрацию обработчиков событий, они будут складываться, когда будущие вызовы тех же операций завершатся, и вы получите непредсказуемые результаты.
  • Оператор States = e.Result выполняется в потоке обработчика событий, который не является потоком диспетчера пользовательского интерфейса. При обновлении списков или сложных свойств это может вызвать проблемы.
  • Как правило, не отменять регистрацию обработчиков событий при их вызове - плохая идея, так как это действительно может привести к трудностям при обнаружении утечек памяти.

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

Если вы считаете, что все это утомительно, посмотрите http://blogs.msdn.com/b/csharpfaq/archive/2010/10/28/async.aspx и http://msdn.microsoft.com/en-us/vstudio/async.aspx. В следующей версии C # будет введено ключевое слово async, чтобы упростить все это. CTP уже доступен.

1 голос
/ 13 октября 2011

Обработчики событий являются смертельными ловушками, и вы их утечете, если не «отписаться» с помощью «- =».

Один из способов избежать - использовать RX (Reactive Extensions), который будет управлять вашими подписками на события. Взгляните на http://msdn.microsoft.com/en-us/data/gg577609 и определенно создайте Observable с помощью Observable.FromEvent или FromAsync http://rxwiki.wikidot.com/101samples.

0 голосов
/ 12 октября 2011

Да, обработчики событий в основном будут вызывать утечку, если не будут удалены. Чтобы получить почти одинарный строковый эквивалент того, что вы выражаете в своем коде, и удалить обработчики, вам понадобится экземпляр некоторого класса, который представляет полный жизненный цикл вызова и занимается уборкой.

Я создал класс Caller<TResult>, который использует базовый клиентский прокси WCF, следуя этому базовому шаблону:

  • создание Caller экземпляра вокруг существующего или нового клиентского прокси (жизненный цикл прокси находится за пределами объема выполняемого вызова (так что вы можете использовать новый недолговечный или существующий долгоживущий) .
  • использует одну из Caller различных перегрузок CallAsync<TArg [,...]>, чтобы указать асинхронный метод для вызова, и предназначенный обратный вызов для вызова после завершения. Этот метод выберет асинхронный метод, который также принимает параметр state . Параметром состояния будет сам экземпляр Caller.
  • Я говорю предназначен , потому что обработчик real , который будет подключен, будет выполнять немного больше служебных операций. Реальный обратный вызов - это то, что будет вызываться в конце асинхронного вызова, и будет
    • проверьте, что ReferenceEquals(e.UserState, this) в вашем реальном обработчике
    • если не верно, немедленно вернуть (событие не было задумано как результат этого конкретного вызова и должно быть проигнорировано; это очень важно, если ваш прокси долгоживущий)
    • в противном случае немедленно удалите реальный обработчик
    • Позвоните на ваш предполагаемый, фактический обратный звонок с e.Result
  • Измените реальный обработчик Caller, чтобы выполнить требуемый обратный вызов в нужном потоке (более важно для WPF, чем Silverlight)

Вышеприведенная реализация также должна иметь отдельные обработчики для случаев, когда e.Error не равен нулю или e.Cancelled равно true. Это дает вам преимущество не проверять эти случаи в вашем предполагаемом обратном вызове. Возможно, ваши перегрузки принимают дополнительные обработчики для этих случаев.

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

Вот пример вызова с использованием класса (вы заметите, что во многих случаях я использую группы методов для повышения читабельности, хотя HandleStuff могло бы быть result => использовать результат ) Первая группа методов важна, поскольку CallAsync получает владельца этого делегата (т. Е. Экземпляра службы), который необходим для вызова метода; в противном случае служба может передаваться как отдельный параметр).

Caller<AnalysisResult>.CallAsync(
    // line below could also be longLivedAnalyzer.AnalyzeSomeThingsAsync
    new AnalyzerServiceClient().AnalyzeSomeThingsAsync,
    listOfStuff,
    HandleAnalyzedStuff,
    // optional handlers for error or cancelled would go here
    onFailure:TellUserWhatWentWrong);
0 голосов
/ 10 октября 2011

К сожалению, это не очень хороший подход.

Я научился этому нелегко в серебряном свете.

Ваш WebserviceAgent, вероятно, является долговечным объектом, тогда как модель или представление, вероятно, недолговечны

События дают ссылки, и в этом случае агент веб-службы и клиент wcf ссылаются на модель. Объект с длительным сроком службы имеет ссылку на объект с коротким сроком службы, это означает, что объект с коротким сроком службы не будет собран, и поэтому будет иметь место утечка памяти.

Как сказал Питер-Биас, асинхронная функциональность облегчит эту задачу.

Вы смотрели на услуги РИА? Это именно та проблема, которую службы RIA разработали для решения

...