Object lifetime

Sep 1, 2009 at 11:54 AM

The PartCreationPolicy allows you to declare a shared or non-shared export. The RequiredCreationPolicy allows you to declare a shared or non-shared import. The MEF CompositionContainer tracks the lifetime of objects, so it will either share an existing instance or create one on every import.

I'm having a difficult time to find the right approach to a typical scenario:

  • a Silverlight application has a main window and lots of views ("document views").
  • at any time the user could have opened several instances of a particular view, but with different instance data. So the user would for instance be able to look at the product details of various products (compare with MDI-concepts).
  • a view has imported dependencies. Think about the View-Model-ViewModel pattern. It might also have a local event aggregator when the view itself is a composite view.
  • the application has a global event aggregator (or better said: message bus). For the sake of the sample, this event aggregator is of the same type as the view event aggregator.

How can I create an object tree per view instance where I have globally scoped, application wide instances and locally scoped instances? Should every dependency be imported or can we use a ServiceLocator for high level dependencies? What is the best approach to hide the CompositeContainer calls?

I assume I have to create a new child CompositeContainer whenever I create a new view instance. So if the user starts a new activity, it will start a new view. The application creates a child container, news up the view and registers the view in the child container. This way I'm able to create a local instance of the view and all of its dependencies. I assume it would make sense for the view to rely on an imported CompositeContainer, so it can use the right container.

An alternative would be to make use of the PartCreator at the view level.  But when the PartCreator creates the part, what will happen when such a type needs to be resolved by the container? Will the container exclude this instance in it's resolution strategy?

The preview 7 drop has an ImageViewer sample where classes call PartsInitializer in their constructor to resolve dependencies. This is great, since now you can simply new up this type and you bypass the CompositeContainer in your code. But how will PartsInitializer resolve the CompositeContainer? When you call the PartsInitializer in the constructor you risk that not all dependent types can be resolved, or?

In the ImageViewer example the View will be constructed via XAML and will be responsible to resolve the ViewModel (in my case), which is good. How can you prevent passing the (child)container reference and still exploit the PartsInitializer?

Sep 1, 2009 at 6:38 PM
Edited Sep 1, 2009 at 6:40 PM

Hi Theo

I would not recommend importing the container for your scenario. I would use a hierachical container though as you mentioned. Then depending on what I needed to do I would either import parts with a Shared creation policy or I would use PartCreator.

For locally scoped exports, I would make sure the parts have a Shared Creation Policy and are in the catalog of the child container. Thus the ViewModel and the locally scoped EventAggregator would all just be shared parts that anyone who wanted could import.

For non-shared transactional things, I would use PartCreator. For example, if I need a new LineItemVM to be created in response to someone pressing the "Add LineItem" button in the composite view, then I would just have the Manager / Coordinator for that view import that PartCreator and create the new VMs on demand.

As I mentioned to you yesterday if you want to hide the complexity of the child containers, you can actually create a custom ExportProvider that wraps the creation of the scoped containers, and delegates to them behind the scenes, but this would require a bit more work. It's not a requirement though.


If you need an actual code example, let me know.


Sep 1, 2009 at 7:37 PM
gblock wrote:

As I mentioned to you yesterday if you want to hide the complexity of the child containers, you can actually create a custom ExportProvider that wraps the creation of the scoped containers, and delegates to them behind the scenes, but this would require a bit more work. It's not a requirement though.

OK, I didn't find documentation in the excellent MEF Programming Guide. But I found this http://blog.eworldui.net/post/2008/11/MEF-2b-Factories-Using-an-Export-Provider.aspx  about ExportProvider. I see the example registers a generic FactoryExportProvider in the root container. With some imagination I understand how the IService can be resolved. I fail to see how this will help you creating child MEF-containers.

If you want to create a code sample, it would be very helpful to me. So let's assume we have an Application class that creates the initial composition container. How would a new window be created when the user presses a button?

class Application
   private CompositionContainer _container;

   public void Bootstrap()
     var catalog = new AttributedAssemblyPartCatalog(Assembly.GetExecutingAssembly()); 
     // ...
     _container = new CompositionContainer(catalog);       
   private void OnCreateWindow(string someWindowName)
      Type concreteType = SomeMappingFromNameToType(someWindowName);
      // What happens next... do we create a new CompositionContainer with a new Catalog? If so, why?


Sep 1, 2009 at 7:54 PM

With an Export Provider, you could override GetExportsCore to evaluate the contracts (looking at the import definition) that the container is being queried for. You could then see it is one of your "scoped contracts", spin up a new child container, and then delegate the call to that child container.


Sep 2, 2009 at 7:23 AM

I think my problem is understanding the shared concept and the lifetime of shared instances. Some quotes from http://mef.codeplex.com/Wiki/View.aspx?title=Parts%20Lifetime&referringTitle=Home:

  • "...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)..." and "...As the root part is just non shared no reference was being kept by the container, so it is basically a no-operation...". This is contradictionary. With "container.ReleaseExport(someObject);" you can release an instance from the container, but it will also dispose the instance.
  • "...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..."
    So what will happen when you create a child container with the same catalog and add contracts? I assume they will end up in the parent container as well.
  • The image of the object graph shows shared and unshared parts. I assume we need to create views as non shared instances. But what about the parts shared in the view object tree? We can use the PartCreator to lazy construct locally scoped parts.

Assume we have an order entry screen/view which has two tab pages, one for the order header and one for the orderlines. The orderlines is simplistic, so there is a table to show all the lines and there is a detailed view. The order entry view is composed of at least three parts like this:
- order header view part
- orderlines table view part
- orderline detail view part

Next there is the OrderEntrtyViewModel which represents the view state. The three view parts share this view model. In our case we would like to submit the entire order when orderlines are added on the client, since things like price calculations depend on the complete order. The simple question is: how do you setup the container/imports/exports, so that you can have multiple order entry view instances and within the view parts you share a single view model. Wouldn't the container recognize the "shared view model" and will only create just one during the lifetime of the composition container?

Sep 2, 2009 at 9:17 AM

By default all parts have a lifetime of shared, thus if you create an OrderEntryViewModel tack on an ExportAttribute and throw it in the contianer, then it is shared by all parts within that container who import it. In your scenario you want to have multiple orders open, with each order having it's own 3 order parts that you mentioned above. As such the configuration you need is to have one container per Order, each container will have a catalog that contains the ViewModel part, as well as the 3 order parts. All the parts in that catalog can have a shared lifetime as each container will only ever have one instance of a VM, Order Header part, Orderlines part, or Orderline detail part.