2012-01-09 50 views
11

Tôi đã gặp phải một số vấn đề trong phần đời của MEF gây rò rỉ bộ nhớ trong ứng dụng Prism của tôi.MEF giữ tham chiếu đến các phần không được chia sẻ IDisposable, không cho phép chúng được thu thập bởi GC

Chế độ xem và chế độ xem xuất của ứng dụng của tôi với PartCreationPolicy được đặt thành CreationPolicy.NonShared. Chế độ xem và chế độ xem được kế thừa tương ứng từ ViewBaseViewModelBase, thực hiện IDisposable.

Bây giờ, vì các bộ phận của tôi thực hiện IDisposable, một tham chiếu đến chúng được lưu giữ bởi vùng chứa, khiến chúng không bị bộ thu gom rác thải ra. Theo MEF documentation on part lifetime, điều này là do thiết kế:

Các container sẽ không giữ tham chiếu đến phần nó tạo ra trừ khi một trong những điều sau đây là đúng:

  • Phần được đánh dấu là Shared
  • Phần thực hiện IDisposable
  • Một hoặc nhiều nhập được định cấu hình để cho phép khôi phục

Làm thế nào sau đó tôi có thể làm cho MEF không giữ một tham chiếu đến các bộ phận này? Có một thuộc tính tôi có thể sử dụng để cho MEF biết tôi không muốn nó giữ một tham chiếu đến phần của tôi ngay cả khi nó thực hiện IDisposable?

Cả hai chiến lược được thảo luận trong bài viết ở trên không có vẻ như giải pháp tốt cho tôi:

  • ReleaseExport đòi hỏi một đối tượng Export như một tham số, mà tôi không biết làm thế nào để cung cấp. Tôi có thể hiện quan điểm của mình, nhưng không có cách nào để tôi biết hợp đồng được sử dụng để tạo ra cái nhìn. Nó sẽ là tuyệt vời nếu có một quá tải cho ReleaseExport mà có thể nhận được bất kỳ đối tượng được tạo ra bởi các container.
  • Sử dụng thùng chứa trẻ em dường như không phải là một lựa chọn tự nhiên.

Mọi trợ giúp sẽ được đánh giá cao.

Trả lời

7

Trừ Prism hỗ trợ một số loại đời để xem đối tượng, không có giải pháp ở đây ngoại trừ việc xóa IDisposable khỏi danh sách giao diện được hiển thị bởi chế độ xem.

Có ba cách tiếp cận MEF để xử lý vấn đề này, tất cả được đề cập bởi phản ứng khác:

  • ExportFactory<T>
  • container Child
  • ReleaseExport()

Tất cả trong số họ yêu cầu một số công việc trên một phần của mã yêu cầu xuất khẩu ban đầu - trong trường hợp mã này trong Prism. Điều này làm cho một số ý nghĩa, vì nó là không mong muốn cho mã tiêu thụ một đối tượng phải được nhận thức như thế nào và khi nó được tạo ra.

Không có ReleaseExportedObject() trong MEF vì nhiều (ví dụ: thuộc tính) xuất có thể trả lại cùng một giá trị; nó có thể được cung cấp một cách hợp lý để cung cấp nhưng sự phức tạp thêm vào làm cho nó không được giải quyết bởi MEF trong tương lai gần.

Hy vọng điều này sẽ hữu ích; Tôi đã lắp lại câu hỏi này 'lăng kính' vì tôi chắc rằng những người khác trong cộng đồng Prism sẽ gặp phải điều này và có thể đưa ra lời khuyên.

+0

Cảm ơn bạn đã trả lời và xin chào mừng. Tôi đoán tích hợp với Prism cùng với 'ExportFactory' là đúng cách để đi, mặc dù nó có vẻ như một overkill cho một yêu cầu đơn giản là" không thêm tôi vào container ". Tôi vẫn chưa từ bỏ - Tôi vẫn đang tìm kiếm một giải pháp đơn giản và thanh lịch hơn cho việc này. –

5

Khi bạn thực hiện IDisposable bạn đang loại nói rằng loại nên được làm sạch lên một cách xác định (bằng cách gọi IDisposable.Dispose và không ngẫu nhiên khi thu gom rác quyết định rằng đó là thời gian.

Trong trường hợp của bạn xem mô hình sẽ chỉ được xử lý khi bạn vứt container mà có lẽ không phải những gì bạn muốn làm để làm được việc này tôi thấy hai giải pháp có thể:..

  • Đừng thực hiện IDisposable trên các mô hình điểm của bạn Rõ ràng bạn don 't quan tâm đến khi họ được làm sạch anyway tại sao làm cho chúng IDisposable?

  • Thay vì cho phép vùng chứa tạo từng kiểu xem không chia sẻ, bạn có thể sử dụng lớp nhà máy chế độ xem được chia sẻ. Sau đó, bạn có thể đưa lớp đó vào chủ sở hữu của các mô hình chế độ xem để cho phép chủ sở hữu tạo rõ ràng các mô hình xem. Giả sử các chủ sở hữu này cũng sẽ biết khi nào nên bỏ các mô hình xem.

Về cơ bản, nếu một thứ gì đó dùng một lần cũng phải là điểm hợp lý trong mã mà bạn cần vứt bỏ đồ dùng một lần.

+0

Tôi tin rằng bạn sẽ đồng ý rằng tôi thực hiện 'IDisposable' một cách ngớ ngẩn để xử lý một đối tượng sớm hơn là sau này và đạt được điều ngược lại chính xác do việc triển khai vùng chứa MEF. Không thực hiện 'IDisposable' trên máy ảo của tôi không phải là một lựa chọn thực vì tôi cần dọn dẹp như hủy đăng ký từ các lệnh ghép - nếu tôi không làm điều đó, các máy ảo sẽ không được GC'd. Quan điểm của tôi phụ trách việc vứt bỏ các máy ảo của họ, nhưng cả hai cách gọi là 'Dispose()' không loại bỏ tham chiếu đến đối tượng khỏi vùng chứa, vì vậy điều đó không giúp ích gì cả. –

+0

Tôi nghĩ rằng việc sử dụng gợi ý thứ hai của tôi về việc đưa một nhà máy chế độ xem vào mỗi chế độ xem thay vì xem mô hình được tạo bởi MEF sẽ giải quyết vấn đề của bạn. Rõ ràng tạo ra một nhà máy cho mỗi mô hình xem trở nên khá tẻ nhạt nhưng tuy nhiên là những gì tôi thấy mình làm rất nhiều khi sử dụng phiên bản hiện tại của MEF cho tiêm phụ thuộc. –

4

Bạn nên tạo các trường hợp này thông qua một ExportFactory<T> được nhập. Sau đó, bạn sẽ có quyền kiểm soát cần thiết để xử lý chúng thông qua ExportLifetimeContext<T>.Dispose().

Tuy nhiên, điều này chỉ có sẵn trong hộp trong phiên bản .NET tiếp theo (4.5) hoặc trong bản phát hành bản xem trước MEF mới nhất trên codeplex. (Trong các phiên bản cũ của MEF các chức năng tương tự đã được thực hiện như một mẫu và được gọi PartCreator, như mô tả trong blog post này.)

+0

Tôi đã không đề cập đến nó trong câu hỏi của mình, nhưng tôi đang sử dụng Prism để tạo ra các khung nhìn của mình, vì vậy tôi không thể truy cập phần đó của mã để lấy ra một đối tượng 'ExportFactory'. Thậm chí nếu tôi có thể, có vấn đề khi truy cập đối tượng factory từ vị trí trong đoạn mã mà tôi muốn loại bỏ khung nhìn và sử dụng 'ExportFactory' cho mỗi đối tượng' NonShared' rất không trực quan và không có vẻ như một thực hành tốt. –

+0

@Lester: Bạn chỉ nên sử dụng ExportFactory cho các đối tượng cần được tạo/xử lý rõ ràng bởi nhà nhập khẩu (cho phép tạo đối tượng có tuổi thọ ngắn hơn so với vùng chứa MEF). Tôi không ** nói rằng bạn nên sử dụng ExportFactory bất cứ nơi nào bạn có một xuất khẩu NonShared bây giờ. –

2

Tất cả các câu trả lời khác đều cung cấp cách tốt để vượt qua vấn đề này, nhưng cuối cùng tôi đã sử dụng giao diện tùy chỉnh của riêng mình, ICleanup, thay vì IDisposable. Điều này tất nhiên có thể không phù hợp cho tất cả mọi người.