2012-01-09 14 views
11

Zdarzyło mi się trochę problemów w czasie życia części MEF, które powoduje wycieki pamięci w mojej aplikacji Prism.MEF zachowuje referencje dla niepoznawanych części IDisposable, nie pozwalając na ich zbieranie przez GC

Moja aplikacja eksportuje widoki i modele widoków z ustawieniem PartCreationPolicy na CreationPolicy.NonShared. Widoki i modele widoku dziedziczą odpowiednio od ViewBase i ViewModelBase, która implementuje IDisposable.

Teraz, ponieważ moje części implementują IDisposable, odniesienie do nich jest przechowywane przez kontener, co powoduje, że nie są one zwalniane przez pojemnik na śmieci. Według MEF documentation on part lifetime, to jest zgodne z projektem:

Pojemnik nie będzie posiadać odniesienia do części to stwarza chyba jeden z następujących warunków:

  • część jest oznaczona jako Shared
  • Część implementuje IDisposable
  • Jeden lub więcej import jest skonfigurowany tak, aby umożliwić Rekompozycja

Jak mogę sprawić, aby MEF nie odwoływał się do tych części? Czy jest jakiś atrybut, którego mogę użyć, aby poinformować MEF, że nie chcę, aby utrzymywał odniesienie do mojej części, nawet jeśli implementuje ona IDisposable?

Obie strategie omówione w powyższym artykule nie wydaje się dobre rozwiązania dla mnie:

  • ReleaseExport wymaga obiektu Export jako parametr, który nie wiem jak zapewnić. Mam instancję mojego poglądu, ale nie ma sposobu, aby dowiedzieć się, jaki był kontrakt, który został użyty do stworzenia poglądu. Byłoby wspaniale, gdyby istniało przeciążenie dla ReleaseExport, które mogłoby odbierać dowolny obiekt utworzony przez kontener.
  • Używanie kontenera podrzędnego również nie wydaje się być naturalnym rozwiązaniem.

Każda pomoc zostanie bardzo doceniona.

Odpowiedz

7

ile Prism obsługuje jakiegoś życia na widok obiektów, nie ma rozwiązania tutaj wyjątkiem usuń IDisposable z listy interfejsów odsłoniętych przez widok.

Istnieją trzy MEF podejście do obsługi tego problemu, wszystkie wymienione przez innych respondentów:

  • ExportFactory<T>
  • pojemników dziećmi
  • ReleaseExport()

Wszystkie z nich wymaga trochę pracy na część kodu, który żąda oryginalnego eksportu - w tym przypadku kod w Prism. Ma to pewien sens, ponieważ niepożądane jest, aby kod konsumujący obiekt musiał być świadomy, jak i kiedy został utworzony.

W MEF nie ma wartości ReleaseExportedObject(), ponieważ wiele wywożeń (na przykład właściwości) może zwrócić tę samą wartość; Logiczne jest, że jest to możliwe, ale dodatkowa złożoność sprawia, że ​​jest mało prawdopodobne, aby w najbliższej przyszłości MEW został rozwiązany.

Mam nadzieję, że to pomaga; Ponownie zastanowiłem się nad tym pytaniem: "pryzmat", ponieważ jestem pewien, że inni członkowie społeczności Prism napotkają to i będą w stanie udzielić porady.

+0

Dziękuję za odpowiedź i za powitalną wiadomość. Sądzę, że integracja z Prismem wraz z 'ExportFactory' jest właściwą drogą, choć wydaje się, że przesada jest prosta:" nie dodawaj mnie do kontenera ". Jeszcze się nie poddałem - wciąż szukam prostszego i eleganckiego rozwiązania. –

5

Kiedy wdrożyć IDisposable jesteś jakby mówiąc, że rodzaj powinny być czyszczone w deterministyczny sposób (poprzez wywołanie IDisposable.Dispose a nie losowo, gdy garbage collector zdecyduje, że nadszedł czas.

W twoim przypadku widoku modele będą usuwane tylko po wyrzucać pojemnika, który prawdopodobnie nie jest to, co chcesz zrobić, aby obejść ten problem widzę dwa możliwe rozwiązania:..

  • nie wdrożyć IDisposable na Państwa zdanie modeli Widocznie ty nie nie przejmuj się, kiedy i tak są sprzątane dlaczego je zrobić IDisposable?

  • Zamiast zezwalać kontenerowi na tworzenie każdego modelu niewspółużytkowanego widoku, można użyć udostępnionej klasy widoku modelu widoku. Następnie można wprowadzić tę klasę do właścicieli modeli widoku, aby umożliwić właścicielom jednoznaczne tworzenie modeli widoku. Prawdopodobnie ci właściciele również wiedzą, kiedy pozbywać się modeli widoków.

Zasadniczo, jeśli coś jest jednorazowego użytku, które powinny być również sensowny punkt w kodzie, gdzie trzeba wyrzucać to, co jest jednorazowy.

+0

Wierzę, że zgodzisz się, że to trochę głupie, że zaimplementuję 'IDisposable', aby pozbyć się przedmiotu wcześniej niż później i osiągnąć dokładnie odwrotny skutek z powodu wprowadzenia kontenera MEF. Niewykonanie 'IDisposable' na moich maszynach wirtualnych nie jest realną opcją, ponieważ muszę wykonać czyszczenie, jak wyrejestrowanie z poleceń złożonych - jeśli tego nie zrobię, maszyny wirtualne nie będą GC. Moje poglądy są odpowiedzialne za unieszkodliwianie ich maszyn wirtualnych, ale tak czy inaczej wywołanie polecenia "Dispose()" nie usuwa odwołania do obiektu z kontenera, więc i tak to nie pomaga. –

+0

Myślę, że za pomocą mojej drugiej sugestii wstrzyknięcia fabryki widoku modelu do każdego widoku zamiast widoku modelu utworzonego przez MEF powinien rozwiązać problem. Oczywiście stworzenie fabryki dla każdego modelu widoku staje się dość nudne, ale mimo to robię to, co robię, kiedy używam obecnej wersji MEF do wstrzykiwania zależności. –

4

Te instancje powinieneś utworzyć zaimportowanym ExportFactory<T>. Będziesz wtedy mieć niezbędną kontrolę, aby pozbyć się ich poprzez ExportLifetimeContext<T>.Dispose().

Jest to jednak dostępne tylko po wyjęciu z pudełka w następnej wersji .NET (4.5) lub w najnowszych wydaniach podglądu MEF na codeplex. (W starszych wersjach MEF taka sama funkcjonalność została zaimplementowana jako próbki i nazwano PartCreator, w sposób opisany w niniejszym blog post.)

+0

Nie wspomniałem o tym w moim pytaniu, ale używam Prism do tworzenia moich widoków, więc nie mogę naprawdę uzyskać dostępu do tej części kodu, aby pobrać obiekt 'ExportFactory'. Nawet jeśli mógłbym, to jest problem z uzyskaniem dostępu do obiektu fabryki z miejsca w kodzie, w którym chcę pozbyć się widoku, i użycie 'ExportFactory' dla każdego obiektu' NonShared', który jest bardzo nieintuicyjny i nie wydaje się być dobra praktyka. –

+0

@Lester: Powinieneś używać ExportFactory tylko dla obiektów, które muszą zostać stworzone/umieszczone bezpośrednio przez importera (który umożliwia tworzenie obiektów o krótszej żywotności niż kontener MEF). Nie jestem ** nie ** mówiąc, że powinieneś używać ExportFactory wszędzie tam, gdzie masz teraz eksport NonShared. –

2

Wszystkie inne odpowiedzi zapewniają dobre sposoby na obejście tego problemu, ale ostatecznie doszłam do tego, że używałam własnego, niestandardowego interfejsu, ICleanup, zamiast IDisposable. To oczywiście może nie być odpowiednie dla wszystkich.