Managed Extensibility Framework Preview 4, a grab bag of goodies.


If you haven’t seen Krys and David’s announcement,  we just released another drop of MEF (I need to hold myself from breaking out in hysterics when I say that ) on CodePlex.


This release contains a whole grab bag of goodies, some of which may initially taste like sour candies (breaking changes), but are sweet in the middle. Most importantly, we’ve done a significant set of enhancements to the MEF codebase around Lifetime, Diagnostics, and Debugging.


Summary of the breaking changes:



  • AllowNonPublicCompositionAttribute was removed. It is no longer needed MEF will always look at publics and non-publics.

  • ComposablePartCatalog was moved from System.ComponentModel.Composition to System.ComponentModel.Compositioni.Primitives.

  • AttributedTypesPartCatalog was renamed to TypeCatalog

  • AttributedAssemblyPartCatalog was renamed to AssemblyCatalog

  • DirectoryPartCatalog was renamed to DirectoryCatalog

  • AggregatingComposablePartCatalog was renamed to AggregateCatalog

  • Catalog Caching extensibility API’s have been made internal.

  • Mutability APIs on the container have been changed to accept a batch. AddPart and RemovePart methods were removed from the container, and you know must pass a CompositionBatch. The advantage of the batch is that you can now replace a single part in the container with a new part in a single operation. You can also add or remove multiple parts in a single operation. This comes in very handy when you have parts that are recomposing.

New features:


New wiki pages


We’ve (well really Hammett) fleshed out all the TBD topics, plus added a ton of new ones both in our programming guide and the arch section.


Diagnostics and debugging


Since joining the team once thing that I’ve heard over and over is just how bad our debugging experience was. Not only did i hear it…I experience it first hand, some times live on stage 🙂 Well fortunately Nick and his feature crew (David and Jad/Zhen) spent the last several months focused on righting this wrong.



We’ve made significant improvements in the debugging experience to address some problematic scenarios and deliver a foundation for diagnostics in all MEF features and extensions.


The differences to note are:



  • Multiple composition errors are structured in numbered groups each relating to a single root cause

  • The ‘causal chain’ (Resulting in: …) traces an issue all the way back to the root action that the application was trying to perform

  • The ‘origin path’ (Element: …) describes how each object involved in the scenario came to be in the composition in the first place

  • All of this information can be retrieved programmatically from the exception types if necessary

Another difference will be the visualization of exceptions when they occur.


Previously you would see something like the following, which was difficult to navigate.


clip_image002


With the changes, you will now see.


clip_image004


Lifetime Management and Creation Policy


Another concern we heard from customers was around how MEF handles lifetime management for parts. In previous releases we allowed the exporter to declare that a part has either a singleton or factory (transient) lifetime.



  • If a transient part implemented IDisposable, it’s resources would be released only when the container was disposed. Singletons would also be disposed in a similar manner. This forces transient parts which could container scarce resources to stay alive. For example if a part contains an unmanaged resource such as a database connection, it will stay open and not get recycled.  This is particularly problematic in server environments such as ASP.NET, where you have a limited number of resources shared by many active sessions. Not having the connections recycled could lead to starvation.

  • Another challenge of the previous approach was that the Part had to determine its lifetime policy. As parts are reusable across different apps, it is very likely that a part in one app is a singleton, while in another it needs to be transient. In the same app there are even cases where the same part needs to be transient in one case and singleton in another. In our previous bits, there was no way to address this.

Hammett and team (Wes, Daniel) have been working tirelessly to come up with a solution to this one. Hammett personally was no stranger to this problem form all the work he’s done on the castle stack. Upon joining the team this is one area that he was relentless about finding a solution to. I am thankful for his passion to see this through as there were several times we hit road blocks that seemed insurmountable.


In the new bits we have provided solutions to both of these problems.



  • We’ve renamed Singleton and Factory creation policy to Shared / NonShared.

  • Parts and Imports can now both declare creation policy. We’ve also added a new policy called Any, which allows a Part or an Import declare that it can work with either policy, i.e. a Part could have a policy of Any, where one  importer of the part’s exports has a policy of NonShared, while another importer has a policy of Shared. The grid below indicates the behavior in different scenarios.
























 


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


An importer specifies the required policy through the use of the new RequiredCreationPolicy as can be seen below.



[Export]
public class Window : System.Windows.Forms.Form
{
    [Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
    public ExportCollection<IShape> Shapes { get; set; }
}
 


  • Parts can now be explicitly released from the container by using the Container.ReleaseExport method. This method takes the export and traverses all its references that are non-shared and disposes of them if they implement IDisposable. This prevents what we call viral disposability where every part in the chain must implement IDisposable in order for the deepest child to be disposed. This is an extremely difficult problem for you to manage, and without this feature the likelihood of memory leaks is high.


For example, below you can see where the jobExport is explicitly released from the container.



var container = new CompositionContainer(…);
var jobExports = container.GetExports<IJob>();
foreach(var jobExport in jobExports)
{
     var jobProcessor = jobExport.GetExportedObject();
     jobProcess.Process();
     container.ReleaseExport(jobExport);
}
 


  • A Non-shared part that has a recomposable import will be held conditionally based on the lifetime of its exports. If  the exports are gc’d the part will be released as well.

Be sure to check out the new wiki page on Lifetime for a much more detailed explanation.


This should be the last release where we introduce major API changes. We’re getting into the home stretch now, with Beta 1 around the corner. There’s still time for us to make fixes though so please get your feedback in.


Special thanks to Hammett for making sure this release got out the door and for the wiki page work. Also thanks to David and Daniel for their supporting efforts on the site. And thanks to the whole team for the awesome work they are doing! Lastly thank to you for your feedback!

Comments (12)

  1. Eric Hauser says:

    Glen,

    Why yet another name for object lifecycles?  Shared and NonShared?  I don’t get why the different containers (and I realize MEF isn’t a traditional IoC container) have different names for such a simple concept.  A couple searches turned up:

    Castle,Singleton,Transient

    Spring.NET,Singleton,Prototype

    StructureMap,Singleton,PerRequest

    Autofaq,Singleton,Factory

    WCF (InstanceContextMode),Single,PerCall

    MEF,Shared,NonShared

  2. Hi Eric

    The more the merrier 🙂 (kidding)

    The reasoning is because in MEF lifetime we are focusing around the ownership of the PART, and not the export itself, as the container only manages part lifetime.

    This is something that is very specific in MEF land, and doesn’t apply to other containers as they don’t have a part notion.

    What do I mean by that? Take a Logger, in an IoC container I register a concrete Logger as ILogger. The type Logger is the thing that is returned from the container when i query it.

    In the MEF however this is not always true though sometimes it is. For example, I can have a class Logger that exports (ILogger) which is added to the container.

    [Export(typeof(ILogger)]

    public class Logger : ILogger

    {

    }

    In this case the Part (Logger) and the export (ILogger) are the same thing, the class.

    However, I can also have property and method exports, in which case the Part is different than the Export. One place I would use this is for exporting sealed types, another is for method exports.

    For example.

    public class CommandManager {

     [Export("SaveCommand")]

     public void Save() {

     }

    }

    In this case for example I am exporting a SaveCommand from a CommandManager. The CommandManager is added to the container. Whenever somone pulls on the container for "SaveCommand", the container will first look to get a CommandManager part. If it is Shared, it will use the same instance, if it is NonShared, it will create a new one.

    Once it has the part, then it can grab the method and create the appropriate delegate to return to the importer.

  3. Bryan Watts says:

    The new drop looks great. I am pleased with the lifetime story and API cleanup. Batching neatly addresses some interesting scenarios.

    A solid effort all-around.

    Question: do you have guidance on import nullability?

    What I mean is: if you have a property of a reference type decorated with [Import], can you assume it will be satisfied, i.e. that the part will be composed via MEF and it will be non-null?

    How do I code a part’s internals (i.e. null-checks on properties) if I am enabling it to be composed via MEF?

  4. Hi Brian

    Thanks for the feedback. We made a bunch of touch decisions this past milestone, it wasn’t easy, but we believe worth it.

    In terms of nullability, you can determine on an import whether or not null is allowed by setting the AllowDefault property of the ImportAttribute.

    If AllowDefault=true, then the import will be optional, and the part will compose whether it can be satisfied or not. We will set the import however to default(T), so if you had an existing value it will be overwritten.

    If AllowDefault=false, then the import will be required and the part will fail composition if the import is not present.

    Now if you want to have actual default values such as a default timeout of 100, the easiest way to do this is to just have a property whose setter checks the value and it is set to the default(T) you just return your default. For example.

    private int _timeout;

    [Import(AllowDefault=true)]

    public property Timeout {

     get { return _timeout !=0 ? _timeout : 100}

     set {_timeout = value;}

    }

    We also have an additional mechanism that allows you to provide defaults for things that you want always to succeed in composition. You do this through through are export provider model. Using an aggregate export provider, you can provide an export provider such as a CatalogExportProvider which returns “defaults”. That provider can be addition to the other catalogs. I am planning to do a blog post on this, but below is an illustration of how you could setup a default logger.

    var catalog = new DirectoryCatalog(“SomePath”);

    var defaultCatalog = new TypeCatalog(typeof(DefaultLogger));

    var defaultEP = new CatalogExportProvider(defaultCatalog);

    var container = new CompositionContainer(catalog, defaultCatalog);

    defaultEP.SourceProvider = container //important

    We’ve now setup a TypeCatalog which provides a default logger.

    Now once you can have the following part.

    public class UsesLogger {

     [ImportingConstructor]

     public UsesLogger(ILogger logger)

     {

     }

    }

    When this part gets composed it will import a logger. That logger will EITHER be the default logger we defined, or an overridden logger that was provided in the directory catalog.

    The reason this works is because the AggregatingExportProvider under the hood in the container has some smartness. When it is queried it uses an ordering mechanism. So the first catalog we passed gets priority over the second.

    Make sense?

    Glenn

  5. Eric Hauser says:

    Glen,

    Thanks for the clarification.

  6. Bryan Watts says:

    Glenn,

    Thank you for explaining how MEF handles nullability, default values, and default imports.

    Let’s say we have this class:

    public class UsesLogger

    {

     public UsesLogger() {}

     [Import]

     public ILogger Logger { get; set; }

     public void DoSomething()

     {

       // …

       Logger.Log("…");

     }

    }

    A unit test would throw a NullReferenceException:

    var ul = new UsesLogger();

    ul.DoSomething();  // error

    This is easily fixed with a null check in DoSomething, but cumbersome, which is the root of my question:

    Should this class be written as though it is always composed via MEF?

  7. Hi Bryan

    For the logger case, why not just use constructor injection?

    In either case if I were testing in a unit test, I would pass in a mock / fake either in a constructor, or through setting the property.

    i.e.

    var ul = new UsesLogger(new FakeLogger())

    or

    var ul = new UsesLogger {Logger = new FakeLogger()}

    In general I would not design MEF parts with an assumption that the container is present during testing.

    The one place where you might require something special as part of your setup is if you are using our Export<T> / Export<T,M> / ExportCollection<T>, ExportCollection<T,M> types as members. In those cases, in your setup / test you will have to pass in a fake which you can simply new up.

  8. Bryan Watts says:

    Glenn,

    > For the logger case, why not just use constructor injection?

    I probably would. I needed an example of something which may be null when used. I guess a property implies an optional import and a null-check.

    >In general I would not design MEF parts with an assumption that the container is present during testing.

    That answers my question. Thanks.

  9. Bryan, the other intersting question that is related is, should you design with the idea that MEF is present at runtime? In this case, the answer IMO is a definite yes. You need to design with an expectation of how MEF will handle things at runtime.

    This is one of the subtleties about using MEF, which is that your parts are not designed to be MEF agnostic, your parts really do depend on MEF’s presence.

  10. Bryan Watts says:

    Ah, I think this hits on what I was really asking. By using the attribute model, you have already admitted knowledge of MEF.

    Since MEF is a framework-level concept, this seems akin to accepting that the CLR is responsible for your object instance.

    Parts are bricks and MEF is the mortar. Got it. Thanks for all the hard work!

  11. @Bryan

    And thanks for all your feedback 🙂

Skip to main content