2013-07-17 4 views
2

Я пытаюсь использовать Ninject (версия 3.0.1) в приложении WinForms, у меня есть несколько (в настоящее время) self-binded класс сервиса, который я создаю с использованием Ninject. Для некоторых классов обслуживания нужны другие классы обслуживания (под-сервисы). Для большинства этих классов обслуживания необходим репозиторий для взаимодействия с базой данных, для чего у меня есть абстрактный интерфейс IRepository. Мне нужно иметь один и тот же репозиторий для всей служебной иерархии в классе службы, поэтому я использую область InCallScope() при привязке IRepository. В настоящее время я использую XPO как инструмент ORM, поэтому у меня есть реализация XpoRepository, с которой я связываюсь. См. Мой other question об этом сценарии.Почему Ninject не выпускает удаленные объекты с помощью InCallScope?

Мои привязки выглядит следующим образом:

Bind<IRepository>().To<XpoRepository>().InCallScope(); 

У меня нет явных ToSelf() привязки для каждого класса обслуживания, поэтому я предполагаю, когда я получаю их от Ninject, они должны иметь переходные рамки, которые я истолковать так как я должен их вручную уничтожить.

Предположим, что у меня есть сервисный класс Services1 и Service2, оба имеют параметр конструктора типа IRepository. Предположим, что Services1 хотел бы использовать некоторые методы Services2, поэтому я добавляю еще один параметр конструктора в Services1 с типом Services2. Без Ninject, я хотел бы сделать:

var repo = new MyRepository(); // implementing IRepository 
var service1 = new Services1(repo, new Services2(repo)); 

Я использую одну из услуг в фоновом потоке (с помощью TPL), в цикле, как это:

while (true) { 
    if (CancellatioPending()) 
     break; 
    using (var service = _kernel.Get<Service1>()) 
    { 
     // do some stuff using the service class 
    } 
    Thread.Sleep(20*1000); 
} 

Я имел такую ​​же структуру до используя Ninject, поэтому я (я думаю) правильно реализовал удаление всех объектов, включая репозитории в правильных местах. Тем не менее, я заметил, что, поскольку я использую Ninject для этого, у меня большая утечка памяти в моем приложении, и он срабатывает каждые 2-3 часа с OutOfMemoryException. Я поставил точку останова внутри цикла и заметил, что кэш Ninject содержит тысячи записей, заполненных объектами XpoRepository. Я полагаю, что они настроены мной, но я не уверен, кто вызвал метод dispose.

Почему Ninject держит эти расположенные объекты? Я ожидал бы, что когда я удалю главную службу в конце блока использования (который является областью объектов IRepository из-за InCallScope()), каждый объект в его области должен быть удален и выпущен Ninject.

EDIT: Перед любыми комментариями или ответами о том, почему этот шаблон не является хорошим, я знаю, что это может быть лучше. Я знаю, что я мог бы извлечь служебные интерфейсы, чтобы на самом деле использовать DI и улучшить тестируемость, и я также знаю, что я должен, вероятно, использовать Func<IRepository> в качестве параметра конструктора и вводить в него, и, как будто каждый сервис может иметь свою собственную ответственность за распоряжение репозиторий. Просто у меня нет времени для таких рефакторингов в настоящее время.

+0

смотри также http://stackoverflow.com/questions/15599325/looking-for-a-ninject-scope-that -behaves-like-inrequestscope –

ответ

4

Ninject выпустит репозиторий, если все следующие вещи истинны:

  • никто не держит ссылку на Service1
  • Service1 сам GC'd (поскольку у вас есть нить сон 20 сек существует высокая вероятность того, что она была повышена до Gen 2, и они выпущены очень редко)
  • Обрезка кеша была выполнена после того, как service1 является GC'd, интервал отсечения кеша по умолчанию составляет 30 секунд. Возможно, вы захотите попробовать более короткий интервал.
  • В качестве альтернативы к предыдущей точке вы можете попробовать силы немедленного высвобождения путем внедрения Ninject.Infrastructure.Disposal.INotifyWhenDisposed в Service1
+0

Благодарим вас за ответ. Что я могу сделать, чтобы избавиться от поведения GC, связанного с 20-секундным сном? Я думаю, что это может вызвать проблемы. –

+0

извлеките использование в другой метод –

+0

На самом деле у меня это в отдельном методе в моем реальном коде, я просто не думал бы, что это имеет значение. Я использую компонент «как таймер» (однопоточный сам по себе, поэтому я начинаю его с TPL), который получает действие для циклического выполнения. Он использует Thead.Sleep между каждым циклом. Я попробовал реализовать интерфейс INotifyWhenDisposed, таким образом кеши передаются, но память составляет около 800 МБ, и похоже, что GC не хочет собирать мои объекты. Явный GC.Collect решает это, но я не хотел бы использовать это слишком много. Вы хоть представляете, что еще может это сделать? –