Share via


ExportFactory in MEF 2 [Alok]

This post discusses features in the preview version of MEF, and some details may change between now and the full release.

In the first version of MEF there are only two notions of lifetime: A shared global lifetime or a per instance lifetime. In MEF 2 Preview 4 new support has been added to enable a finer grained control over the lifetime and sharing of parts. Before we can cover those changes in detail, we need to introduce ExportFactory<T> .

Let us consider a simple application scenario of a RequestListener, which spawns a RequestHandler, which uses a DatabaseConnection to connect to a data source (Fig 1):

Let us also consider the following parts that implement the RequestHandler and DatabaseConnection.

     [Export]
    public class RequestHandler
    {
        [ImportingConstructor]
        public RequestHandler(DatabaseConnection connection)
        {
        }
    }

    [Export]
    public class DatabaseConnection
    {
        public DatabaseConnection()
        {
        }
    }

In MEF version 1...

To better illustrate the new capabilities we’ve added in MEF 2, some of the scenarios that could be achieved in MEF version 1 are discussed below.

Scenario 1: RequestHandler and DatabaseConnection instances are shared

When we first start building our app, we are being very conservative about resources and not knowing our load we spin up a single instance of RequestHandler and DatabaseConnection

We could instantiate an instance of the RequestHandler using the following code.

 TypeCatalog global = new TypeCatalog(
    typeof(RequestHandler), typeof(DatabaseConnection));
var container = new CompositionContainer(global);
var requestHandler = container.GetExportedValue<RequestHandler>();

Regardless of the number of times GetExportedValue() is called, the same instance of the RequestHandler is always returned (Fig 2).

Scenario 2: Separate instances of RequestHandler and DatabaseConnection

Now we realize that the throughput for our system is really poor, since we can only process one request at a time. So in order to increase our throughput, we spin up new instance of RequestHandler and DatabaseConnection for every request that comes in. In order to achieve this with MEF we can put a PartCreationPolicyAttribute on RequestHandler and DatabaseConnection:

     [PartCreationPolicy(CreationPolicy.NonShared)]
    public class RequestHandler { … }

    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class DatabaseConnection { … }

Now, with the same invoking code to GetExportedValue() , we now get new instances for RequestHandler and DatabaseConnection every time.

Scenario 3: RequestHandler instances created and DatabaseConnection instance is shared

Our throughput is now up, but creating a new database connection per request is really taking a toll on our database server which can handle only a few open connections at a time. To ease the load on our database server we hence decide, that we will share a single instance of a DatabaseConnection, among multiple RequestHandlers. This will give us our throughput improvements without overloading our database server. We can achieve the following by getting rid of the PartCreationPolicy we have on the DatabaseConnection class, so it would now look like the following:

     public class DatabaseConnection { … }

With the removal of this attribute, we now get only a new instance of the RequestHandler every time GetExportedValue() is called, which all share a single instance of the DatabaseConnection.

In MEF 2... introducing ExportFactory<T>

The above scenarios that we have seen are the sharing scenarios that could be achieved using MEF in the past releases. However there is one glaring omission in the matrix that we presented above. What if I wanted a multiple instance of a dependency from a single instance of what it was depending on? To put it in the context of our example, how does the RequestListener class which was the first block in our diagram create multiple instances of the RequestHandler.

This brings us to:

Scenario 4: Single instance of RequestListener and multiple (dynamic) instances of RequestHandler

In order to enable this scenario, in the latest version of MEF, we introduce a new class called the ExportFactory<T> . ExportFactory<T> may be familiar to Silverlight 4 developers, since it was shipped as a part of the Silverlight 4 SDK. The export factory allows us to create new instances of dependencies and control the lifetime of the created parts.

Let us go back to our initial block diagram and write out the code for the RequestListener class.

     [Export]
    public class RequestListener
    {
        ExportFactory<RequestHandler> _factory;

        [ImportingConstructor]
        public RequestListener(ExportFactory<RequestHandler> factory)
        {
            _factory = factory;
        }

        public void HandleRequest()
        {
            using (var instance = _factory.CreateExport())
            {
                instance.Value.Process();
            }
        }
    }

Now we see in the code that instead of importing a RequestHandler as a depedency, we import an ExportFactory<RequestHandler> . MEF treats ExportFactory<T> as a special class, and will automatically hook it up to the part providing T. We then added a HandleRequest() member, which does the work of instantiating the RequestHandler. The CreateExport() method creates an ExportLifetimeContext<T> , which implements IDisposable, which is used to control the lifetime of the objects created by the factory. Since the listener is creating the parts, we can get also get rid of the PartCreationPolicy attribute on the RequestHandler. Putting all the pieces together a call to:

 TypeCatalog global = new TypeCatalog(typeof(RequestHandler), typeof(DatabaseConnection) , typeof(RequestListener));
var container = new CompositionContainer(global);
var requestListener = container.GetExportedValue<RequestListener>();
requestListener.HandleRequest();

will result in the following composition graph:

Stay tuned for part two of this post where we talk about some more sharing scenarios and how we can use the new scoping enhancements in MEF in conjunction with ExportFactory<T> to accomplish these.

Comments

  • Anonymous
    November 21, 2011
    Hi, I'm glad to see the way MEF is going and the fact that more and more people are using MEF even when extensibility is not an external request but internal need. I also like the fact that Microsoft MEF and Google Guice libraries are getting more and more closer to each other - and this raise the question why not simply go ahead and use ExportFactory<T> the way guice's FactoryModuleBuilder is built. It is quite obvies that the next request after ExportFactory will work is to pass some state into the part that needs to be created. Google already solve that. Keep up the great work. Thank you, Ido.

  • Anonymous
    November 30, 2011
    Hi Ido, thanks very much for the encouragement. You might be interested to find that parameterized construction is supported in different forms by many composition frameworks on .NET as well. This scenario hasn't shown up frequently in requests from MEF users though - do you find you need parameterized construction often? If so it would be interesting to hear more about where this is necessary. Regards, Nick

  • Anonymous
    December 15, 2011
    The comment has been removed

  • Anonymous
    December 19, 2011
    Hi Fyodor - I've responded via your similar question on Stack Overflow: stackoverflow.com/.../8547204 In short, we still have some work to do in this area and will be paying your scenario close attention. There's also some additional information now on this blog describing CompositionScopeDefinition in more detail: blogs.msdn.com/.../sharing-with-compositionscopedefinition-in-mef2-alok.aspx