Parts Lifetime

It’s very important that one understand a parts lifetime within a MEF container and its implications. Given that MEF focuses on open ended applications this becomes especially important as the application authors won’t have control over the set of parts once the app ships and third party extensions come to play. Lifetime can be explained as being the desired “shareability” of a part (transitively, its exports) which translates to the policy that controls when a new part is created as well as when the part will be shut down or disposed.

Shared, Non Shared and ownership

The “shareability” of a part is defined through the CreationPolicy set (class level) using the PartCreationPolicyAttribute. The following values are supported:
  • Shared: the part author is telling MEF that at most one instance of the part may exist per container.
  • NonShared: the part author is telling MEF that each request for exports of the part will be served by a new instance of it.
  • Any or not supplied value: the part author allows the part to be used as either “Shared” or “NonShared”.

The Creation Policy can be defined on a part using the [System.ComponentModel.Composition.PartCreationPolicyAttribute]:

[PartCreationPolicy(CreationPolicy.NonShared)]
[Export(typeof(IMessageSender))]
public class SmtpSender : IMessageSender
{
}
<PartCreationPolicy(CreationPolicy.NonShared), Export(GetType(IMessageSender))> 
Public Class SmtpSender
    Implements IMessageSender

End Class

The container will always have the ownership of parts it creates. In other words, the ownership is never transferred to an actor that requested it by using the container instance (directly) or through an import (indirectly).
Imports can also define or constraint the creation policy of parts used to supply the import values. All you have to do is specify the CreationPolicy enum value for RequiredCreationPolicy:

[Export]
public class Importer
{
    [Import(RequiredCreationPolicy=CreationPolicy.NonShared)]
    public Dependency Dep { get; set; }
}
<Export()>
Public Class Importer
    <Import(RequiredCreationPolicy:=CreationPolicy.NonShared)> 
    Public Property Dep() As Dependency
End Class

This is a useful for scenarios where the “shareability” of a part is relevant for the importer. By default, the RequiredCreationPolicy is set to Any, so Shared and NonShared parts can supply the values.
The following table summarizes the behavior:

- Part.Any Part.Shared Part.NonShared
Import.Any Shared Shared Non Shared
Import.Shared Shared Shared No Match
Import.NonShared Non Shared No Match Non Shared


Note that when both sides define “Any” the result will be a shared part.

Disposing the container

A container instance is generally the lifetime holder of parts. Part instances created by the container have their lifetime conditioned to the container’s lifetime. The way to signal the end of the container lifetime is by disposing it. The implications of disposing a container are:
  • Parts that implement IDisposable will have the Dispose method called
  • Reference to parts held on the container will be cleaned up
  • Shared parts will be disposed and cleaned up
  • Lazy exports won’t work after the container is disposed
  • Operations might throw System.ObjectDisposedException

Container and parts references

We believe that .Net Garbage Collector is the best thing to rely on for proper clean up. However, we also need to provider a container that has a deterministic behavior. Thus, the container will not hold references to parts it creates unless one of the following is true:
  • The part is marked as Shared
  • The part implements IDisposable
  • One or more imports is configured to allow recomposition

For those cases, a part reference is kept. Combined with the fact that you can have non shared parts and keep requesting those from the container then memory demand can quickly become an issue. In order to mitigate this issue you should rely on one of the following strategies discussed in the next two topics.

Scoped operations and early reclaim of resources

Some common kinds of applications like web apps and windows services vary greatly from desktop applications. They are more likely to rely on batched and short lived operations. For example, a windows service might watch a directly and once a pre-determined number of file is present, start a batching operation that transforms those files into another format. Web operations may be scoped by per-web-request operations.

For those scenarios you should either use child containers (explained in the next topic) or release early the object graph. The latter allows the container to dispose and clear out references to non shared parts in the object graph – until it reaches a shared part.

In order to early release the object graph you need to call the method ReleaseExport exposed by the CompositionContainer:

var batchProcessorExport = container.GetExport<IBatchProcessor>();

var batchProcessor = batchProcessorExport.Value;
batchProcessor.Process();

container.ReleaseExport(batchProcessorExport);
Dim batchProcessorExport = container.GetExport(Of IBatchProcessor)() 
Dim batchProcessor = batchProcessorExport.Value
batchProcessor.Process()
container.ReleaseExport(batchProcessorExport) 

The figure below depicts an object graph and show what parts would be released (references removed, disposed) and the ones that would be left untouched:

releaseexp.png

As the root part is just non shared no reference was being kept by the container, so it is basically a no-operation. We proceed traversing the graph checking the exports served to the root part. Dep 1 is both non shared and disposable, so the part is disposed and its reference is removed from the container. The same happens with Dep 2, however, the export used by Dep is left untouched as it is shared – so other parts may be using it.

Note that the implementation traverses the graph in a depth-first way.

Container hierarchies

Another way to approach the same problem is to use container hierarchies. You can create containers and connect them to a parent container, making them child containers. Note that unless you provide a different catalog to the child container, it wouldn’t be of much help as instantiation will continue to happen on the parent.
Hence, what you should do is either filter the parent catalog based on a criterion that divides the set of parts that should be created on the parent container from those that should be created on the child, or you should specify a completely new catalog that expose a set of parts that should be created on the child container. As the child is expected to be short lived, parts created in it will be released and disposed earlier.
A common approach is to have Shared parts created on the parent container and Non Shared on the child container. As Shared parts may depend on exports supplied by Non Shared, then the main catalog will have to contain the whole set of parts whereas the child container should have a filtered view of the main catalog with only the non shared parts.

catalogs.png

For more information on this topic please check Filtering Catalogs

Disposal ordering

Disposal ordering is not guaranteed in any way. That means that you should not try to use an import in your Dispose method. For example:

[Export]
public class SomeService : IDisposable
{
    [Import]
    public ILogger Logger { get; set; }
    
    public void Dispose()
    {
         Logger.Info("Disposing"); // might throw exception!
    }
}
<Export()>
Public Class SomeService
    Implements IDisposable
    <Import()>
    Public Property Logger() As ILogger

    Public Sub Dispose()
        Logger.Info("Disposing") ' might throw exception! 
    End Sub
End Class

Using the imported logger instance on your dispose method implementation may be a problem as the implementation of the ILogger contract may also be disposable, and as such may have been disposed already.

AddPart/RemovePart

Not every part is created by the container. You can also add and remove parts from it. This process triggers composition and may start creating parts to satisfy dependencies of the part added recursively. When the part added is removed MEF is smart enough to reclaim the resources and dispose the non shared parts used by the part added.

Note: that MEF will never take ownership of an instance supplied by you, but it does have the ownership of part it creates to satisfy your instance’s imports.

using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;

class Program
{
    static void Main(string[] args)
    {
        var catalog = new AssemblyCatalog(typeof(Program).Assembly);
        var container = new CompositionContainer(catalog);
        var root = new Root();

        // add external part
        container.ComposeParts(root);

        // ... use the composed root instance

        // removes external part
        batch = new CompositionBatch();
        batch.RemovePart(root);
        container.Compose(batch);
    }
}

public class Root
{
    [Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
    public NonSharedDependency Dep { get; set; }
}

[Export, PartCreationPolicy(CreationPolicy.NonShared)]
public class NonSharedDependency : IDisposable
{
    public NonSharedDependency()
    {
    }

    public void Dispose()
    {
        Console.WriteLine("Disposed");
    }
}
Imports System
Imports System.ComponentModel.Composition
Imports System.ComponentModel.Composition.Hosting
Imports System.ComponentModel.Composition.Primitives

Friend Class Program
    Shared Sub Main(ByVal args() As String) 
        Dim catalog = New AssemblyCatalog(GetType(Program).Assembly) 
        Dim container = New CompositionContainer(catalog) 
        Dim root_Renamed = New Root()

        ' add external part
        container.ComposeParts(root_Renamed) 

        ' ... use the composed root instance

        ' removes external part
        batch = New CompositionBatch()
        batch.RemovePart(root_Renamed) 
        container.Compose(batch) 
    End Sub
End Class

Public Class Root
    <Import(RequiredCreationPolicy:=CreationPolicy.NonShared)> 
    Public Property Dep() As NonSharedDependency
End Class

<Export(), PartCreationPolicy(CreationPolicy.NonShared)> 
Public Class NonSharedDependency
    Implements IDisposable
    Public Sub New()
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        Console.WriteLine("Disposed")
    End Sub
End Class

Last edited Aug 9, 2010 at 5:30 PM by haveriss, version 15

Comments

awhite Jan 25, 2011 at 11:14 PM 
Spelling error: "might watch a directly" should be "might watch a directory"

kbrowns Aug 6, 2010 at 10:43 AM 
We've been using MEF for some time now and love it's capabilities. However, I must say - the API is confusing to me. Conceptually, it makes perfect sense, but I consistently get tripped up on the terminology and class level API. After reading this guide for the 4th time I think I finally figured out what I'm having problems with:

1) the term "Export". When I hear this word, I think of other half of Import - what satisfies an Import. If I"m understanding this correct, it is that, but it's also the umbrella attribute for how to enlist in MEF services. This finally clicked for me when re-reading this page: http://mef.codeplex.com/wikipage?title=Declaring%20Exports&referringTitle=Exports%20and%20Metadata. When I first saw this example:

public class Configuration
{
[Export("Timeout")]
public int Timeout
{
get { return int.Parse(ConfigurationManager.AppSettings["Timeout"]); }
}
}
[Export]
public class UsesTimeout
{
[Import("Timeout")]
public int Timeout { get; set; }
}

It sees very straight forward at first glance "Configuration" is exporting something to "UsesTimeout" so the "Configuration" class has an [Export] attribute on the property it's exporting, and the "UsesTimeout" class has an [Import] property on the Timeout property... but WAIT... UsesTimeout also has an [Export] attribute at the class level... huh? Why? It's not exporting anything... only importing.

As far as I can tell the [Export] is overloaded - it's how an import enlists in the composition process... the [Import] attribute on the property alone is not enough. Is that correct? Based on what I'm reading here and my dabbling, that's correct. Assuming I'm not off base there - that seems really odd to me and each time I put MEF down and then pick it back up and I have to retrain my brain to think about this correctly.

2) The other thing that really trips me up is the overloaded duties of container.Compose() and container.ComposeParts(). So you add and remove things from the contain with the ComposeParts/Compose methods? You also trigger the composition deterministically by calling the same methods. Adding parts to the container by calling ComposeParts() really confuses me. I'd much rather see a DI container style .RegisterInstance() or .AddPart() method.

That's my $0.02... but all in all, I love this library - there just seems to be some overloaded terms and API members IMO.

pawcon Jul 9, 2010 at 11:10 AM 
The "AddPart/RemovePart" example does not compile (even with the missin 'var' correction). Using VS2010 and .NET4.0 I get the following compile error:
Error 1 The best overloaded method match for 'System.ComponentModel.Composition.Hosting.CompositionBatch.RemovePart(System.ComponentModel.Composition.Primitives.ComposablePart)' has some invalid arguments
Error 2 Argument 1: cannot convert from 'Root' to 'System.ComponentModel.Composition.Primitives.ComposablePart'
What is the correct way to remove a part?

brettryan Feb 4, 2010 at 6:10 AM 
In the "AddPart/RemovePart" example there is a missing `var' for `batch = new CompositionBatch();', should be `var batch = new CompositionBatch();'

haveriss Aug 24, 2009 at 7:05 PM 
Because it's not actually removing the part. We have similar methods names in CompositionBatch.

dvanderboom Aug 18, 2009 at 4:23 PM 
Why not just container.RemovePart(root)?

nblumhardt Aug 5, 2009 at 8:04 PM 
@disneystar thanks!

disneystar Aug 5, 2009 at 5:44 PM 
In the text - "The implications of disposing a container is" the "is" should be "are"