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.