Lazy Exports

During composition of a part, an import will trigger the instantiation of a part (or parts) that expose the necessary exports required for the original requested part. For some applications delaying this instantiation – and preventing the recursive composition down the graph – may be an important factor to consider as creation a long and complex graph of objects can be expensive and unnecessary.

This is the motivation for MEF to support what we call lazy exports. In order to use it all you need to do is to import an [System.Lazy<T>] instead of [T] directly. For example, consider the code snippet bellow:

public class HttpServerHealthMonitor
{
    [Import]
    public IMessageSender Sender { get; set; }
Public Class HttpServerHealthMonitor
    <Import()>
    Public Property Sender() As IMessageSender

The code above imports states that it depends on a contract (IMessageSender) implementation. When MEF supply this dependency it will need to also create the IMessageSender selected and recursively the dependencies that the implementation might have.
In order to turn this import to be lazy, you just need to replace it by and Lazy<IMessageSender>:

[Export]
public class HttpServerHealthMonitor
{
    [Import]
    public Lazy<IMessageSender> Sender { get; set; }
<Export()>
Public Class HttpServerHealthMonitor
    <Import()>
    Public Property Sender() As Lazy(Of IMessageSender) 

In this case you are opt-in for delaying this instantiation until you actually need the implementation instance. In order to request the instance, use the property [Lazy<T>.Value].

Last edited Aug 9, 2010 at 5:18 PM by haveriss, version 4

Comments

DanyR Sep 1, 2011 at 12:06 PM 
@threeputt

I believe that the extraneous [Export] on the importing class is used for the Composition Analysis Tool (Mefx), so that the [Import]s will also be listed.

threeputt Aug 25, 2010 at 9:31 PM 
The Lazy coding example includes an [Export]. I assume that was a cut&paste issue rather than a requirement for Lazy exports. If it is required then that is worth a mention in the doc and if its not required then it should be removed just so it does not cause the reader to ponder way it was added to an otherwise duplicate of the previous example that did not include the Export attribute.

While I am at it, is it just me or should this capability really be called Lazy Import rather than Lazy Export... Export is the advertisement of the existance of a part and is passive in the import action. Its the import thread of control that really cares about the Lazy initialization. What do you think?

edore Jul 21, 2010 at 2:15 AM 
Personnally I prefer a generic class than parameters added to an attribute. I'm sure the implementation is far more elegant!

BTW, I wonder if Imports are thread-safe? What happens in a multi-threaded scenario where the a singleton which imports a property is used by 2 threads of the same app domain?

Thanks for your answer!

hyffrank May 28, 2010 at 3:16 AM 
This looks ugly, why not putting the lazy tag into attribute too?
Such as [Import(LazyLoad=true)] ?

RoystonS Feb 6, 2010 at 11:48 AM 
hendryluk: A dynamic proxy Lazy import for interfaces would be nice, but you'd still need the explicit Lazy import specifier for types that can't be proxied (e.g. int or string). (And you might want them to be imported lazily because they're being computed on demand by some expensive process.)

Also, you sometimes need Lazy initialization for reasons other than performance 'tweaks': I most often use delayed injection to break circular dependencies if you're using constructor injection.

damonrpayne Sep 20, 2009 at 5:59 PM 
I would prefer some automagic way of doing this, but since it is the _client_ specifying their special use of the Import it seems reasonable for the client to have one extra "." The cast only helps if you are assigning an instance; in the case of imported modules it's probably more common to use syntax like ImportedService.DoSomething(); too bad we can't do something like public class Lazy<T> : T where T : class

dvanderboom Aug 18, 2009 at 3:52 PM 
How about adding an implicit operator overload to Lazy<T> so that when I change a T to Lazy<T>, I can write:

int result = SomeLazyInt;

Or even more so, for Lazy method delegates, so I can write:

SomeLazyDelegateVar(...);

instead of:

SomeLazyDelegate.Value(...);

Does MEF support lazy method delegates?

nblumhardt Jul 7, 2009 at 3:48 PM 
Thanks for your input. I've udated the page to reflect the version coming with Preview 6, which uses the new System.Lazy<T> type.

Dynamic proxy is definitely an interesting possibility, but unfortunately this techique is limited in the types that can be proxied (as I'm sure you are aware.) Lazy<T> provides this functionality for all types, including sealed types and classes with non-virtual members.

hendryluk Jul 6, 2009 at 7:26 AM 
Shouldn't lazy-export really be implemented as dynamic-proxy? I think lazy or eager is performance-related tweak which should be easily _configured_, without affecting client-code (i.e. with Export<T>.GetExportedObject(), which is also an annoying code-noise).

I prefer something like:
[Import]
[Lazy]
public IMessageSender Sender { get; set; }

Which will create a proxy for the interface. When any properties/methods of the proxy is invoked, it should trigger instantiation of the implementor.

This also gives possiblity to configure Lazy-Export at export side (which is probably more sensible in most cases), without modifying anything on client code (import side), if thats what we want.

Additionally, proxy approach also gives a possiblity to have multiple implementors behind single dependency (if that's what we want).
E.g. Sender.Send() with Proxy could send message to all implementors (like multicast-delegate). That may answer question of what would happen to non-collection dependencies if we introduce more than 2 implementors.

Either way, Export<T> looks like superfluous infrastructure noise to me.