Changing default creation policy from shared (singleton) to non-shared (transient)

One of the things that you might run across when starting to work with MEF, is that by default, all parts are created as singleton (‘shared’) instances.

For example, given the following part:

[Export]
public class MyView
{
}

Running the following code:

TypeCatalog catalog = new TypeCatalog(typeof(MyView));
CompositionContainer container = new CompositionContainer(catalog);

var view1 = container.GetExportedObject<MyView>();
var view2 = container.GetExportedObject<MyView>();

Console.WriteLine("Are same object? " + (view1 == view2));

Outputs:

Are same object? True

In certain scenarios, such as web applications, you typically want the reverse, that is, a new instance (‘non-shared’) of a part created on every request.

We allow you to indicate that, using the CompositionOptionsAttribute:

[Export]
[CompositionOptions(CreationPolicy=CreationPolicy.NonShared)]
public class MyView
{
}

Unfortunately, if you have a lot of them, this can be a bit of a hassle to do this for every part. As luck would have it, the latest drop of MEF allows you modify the default with a little bit of code:

public class TransientCompositionContainer : CompositionContainer
{
    public TransientCompositionContainer(ComposablePartCatalog catalog)
        : base(catalog)
    {
    }

    protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition)
    {
        definition = AdaptDefinition(definition);
        
        return base.GetExportsCore(definition);
    }

    private ImportDefinition AdaptDefinition(ImportDefinition definition)
    {
        ContractBasedImportDefinition namedDefinition = definition as ContractBasedImportDefinition;
        if (namedDefinition != null && namedDefinition.RequiredCreationPolicy == CreationPolicy.Any)
        {   // Only change the creation policy if the importer/requester did not specify one
 
            definition = new ContractBasedImportDefinition(namedDefinition.ContractName,
                                                           namedDefinition.RequiredMetadata,
                                                           namedDefinition.Cardinality,
                                                           namedDefinition.IsRecomposable,
                                                           namedDefinition.IsPrerequisite,
                                                           CreationPolicy.NonShared);
        }

        return definition;
    }
}

Basically what is happening above is that all requests to the container are routed through GetExportsCore. When we see a request (represented as an ImportDefinition) that states that it does not have a preference for a particular creation policy (which is the default when querying from the container, or when using the [Import] attribute), we create a new request that explicitly specifies that it wants a non-shared instance and route that back to the base.

Running the above sample, substituting the standard container with the transient container, should now output:

Are same object? False

There we have it – a container that defaults to non-shared parts. You can download the container below.

Published Saturday, February 07, 2009 7:00 AM by David Kean
Filed under:

Comments

Monday, February 09, 2009 12:35 AM by Ged Moretta

# re: Changing default creation policy from shared (singleton) to non-shared (transient)

Nice article, unfortunately the formatting doesn't allow me to see all of the code you've posted :-(

Any chance you can reformat it so all of the code is visible?

(I'm using Firefox 3.0.6 but the same problem occurs in IE7)

By not being able to see all the code, what do you mean? I'm using IE7 and I can't repro the problem. Anyway, as a workaround, I've attached the source code for the TransientCompositionContainer to the post.