Извиняюсь за довольно многословный и многословный пост, но эта проблема озадачивает меня уже несколько недель, поэтому я публикую как можно больше информации, чтобы быстро решить эту проблему.
У нас есть WPF UserControl
, который загружается сторонним приложением.Стороннее приложение - это презентационное приложение, которое загружает и выгружает элементы управления по расписанию, определенному XML-файлом, который загружается с сервера.
Наш элемент управления, когда он загружается в приложение, отправляет веб-запросвеб-сервис и использует данные из ответа для отображения некоторой информации.Мы используем архитектуру MVVM для управления.Точка входа элемента управления - это метод, который реализует интерфейс, предоставляемый основным приложением, и именно здесь настраивается конфигурация элемента управления.Здесь я также установил DataContext
нашего элемента управления на MainViewModel
.
. MainViewModel
имеет две другие модели представления в качестве свойств, а основной UserControl
имеет два дочерних элемента управления.В зависимости от данных, полученных от веб-службы, основной UserControl
решает, какой дочерний элемент управления отображать, например, если есть ошибка HTTP или полученные данные недействительны, то отображать дочерний элемент управления A, в противном случае отображать дочерний элемент управления B. Каквы ожидаете, что эти два дочерних элемента управления связывают две отдельные модели представления, каждая из которых имеет свойство MainViewModel
.
Теперь дочерний элемент управления B (который отображается, когда данные действительны) имеет свойство / поле RefreshService
.RefreshService
- это объект, который отвечает за обновление модели различными способами и содержит 4 System.Timers.Timer
с;
- a
_modelRefreshTimer
- a
_viewRefreshTimer
- a
_pageSwitchTimer
- a
_retryFeedRetrievalOnErrorTimer
(включается только когда что-топроисходит сбой при получении данных).
Здесь следует упомянуть, что существует два типа данных;первая меняется каждую минуту, вторая меняется каждые несколько часов.Конфигурация элементов управления определяет, какой тип мы используем / отображаем.
Если данные относятся к первому типу, то мы обновляем модель довольно часто (каждые 30 секунд), используя события _modelRefreshTimer
.
Если данные относятся ко второму типу, то мы обновляем модель через более длительный интервал.Однако представление по-прежнему необходимо обновлять каждые 30 секунд, поскольку устаревшие данные необходимо удалять из представления (следовательно, _viewRefreshTimer
).
Элемент управления также разбивает данные на страницы, поэтому мыможно увидеть больше, чем мы можем уместить на дисплее.Это работает, разбивая данные на списки и переключая свойство CurrentPage
(которое является списком) модели представления в правый список.Это делается путем обработки события Elapsed _pageSwitchTimer
.
Теперь проблема
Моя проблема в том, что элемент управления, когда он удален из визуального дерева, не избавляется от своих таймеров.Впервые это было замечено, когда мы начали получать необычно большое количество запросов на веб-сервере, которые заканчивались очень скоро после развертывания этого элемента управления, и обнаружили, что запросы выполняются как минимум раз в секунду!Мы обнаружили, что таймеры жили и не останавливались несколько часов после того, как элемент управления был удален из поля зрения, и чем больше таймеров, тем больше запросов накапливалось на веб-сервере.
Моим первым решением было внедрение IDisposable
для RefreshService
и выполните некоторую очистку, когда сработало событие UnLoaded
элемента управления.В методе Dispose RefreshService
s я установил Enabled
на false
для всех таймеров, затем использовал метод Stop()
на всех них.Затем я тоже позвонил Dispose()
и установил для них null
.Ничего из этого не сработало.
После некоторого прочтения я обнаружил, что обработчики событий могут хранить ссылки на таймеры и предотвращать их удаление и сбор.После некоторого прочтения и исследования я обнаружил, что лучший способ обойти это - использовать Weak Event Pattern .Используя этот блог и этот блог, мне удалось обойти недостатки в шаблоне Weak Event.
Однако это не решает проблему. Таймеры все еще не отключены или не остановлены (не говоря уже о ликвидации), и веб-запросы продолжают накапливаться. Mem Profiler говорит мне, что «у этого типа есть N экземпляров, которые напрямую укоренены делегатом. Это может указывать на то, что делегат не был правильно удален» (где N - количество экземпляров). Насколько я могу судить, все слушатели события Elapsed для таймеров удаляются во время очистки, поэтому я не могу понять, почему таймеры продолжают работать.
Спасибо за чтение. С нетерпением жду ваших предложений / комментариев / решений (если вы получили это далеко :-p)