MEF/Lifetime: NonShared support

November 10th, 2008

MEF currently support two creation policies: Singleton and Factory. You can think of them as Singleton and Transient or even better: Shared and NonShared. Supporting NonShared is very convenient to users, but comes with costs. So we are considering supporting only Singletons in V1.

In the end, all these control your choice of how state should be shared or not between consumers. If you expose an IEmailSender implementation, whether it should be shared between users or not will depend on how much you care about sharing or not the state – if any – in the service implementation.

It doesn’t mean that you can’t mimic NonShared behavior. It just means that you have to write a bit more code in order to do that.

Say that you have a Foo component, and you want that each “consumer” of Foo to have their own exclusive, non-shared copy of it:

public class MyObject
{
    [Import]
    public FooFactory Factory { get; set; }
    
    private Foo _foo;
    public Foo Foo 
    { 
        get 
        { 
            if (_foo == null) 
            {
                _foo = Factory.CreateFoo();
            } 
            return _foo;
        }
    }

    public void DoWork()
    {
        // Do work with Foo
    }
}

So MyObject cannot simply import Foo. It needs to import something that carries the semantic of creating always a new instance. So it imports a Factory that you created.

Now you probably have a Foo that has dependencies. Fair enough. You can support that using at least two approaches.

In the first scenario, you know before hand the dependencies:

public class Foo
{
    public Foo(Baz baz)
    {
       Baz = baz;
    }

    public Baz Baz { get; set; }

    public Bar Bar { get; set; }
}


public class FooFactory
{
    [Import]
    public Bar Bar { get; set; }

    [Import]
    public Baz Baz { get; set; }

    public Foo CreateFoo()
    {
        var foo = new Foo(Baz);

        foo.Bar = Bar;

        return foo;
    }
}

The second situation you do not know the dependencies:

public class Foo
{
    // Assume Foo has unknown dependencies
}

public class FooFactory
{
    [Import]
    public ICompositionService CompositionService { get; set; }

    public Foo CreateFoo()
    {
        var foo = new Foo();

        CompositionService.SatisfyImports( CompositionServices.CreateAttributedPart(foo));

        return foo;
    }
}

We are really looking for feedback on this one. Do you think that’s reasonable? Would you be angry or frustrated if you had to write this code? And before you ask, we are not considering ripping off the feature because we don’t have anything better do to. :-) It all comes down to the fact that supporting factories lead to situations that we are unable to provide good solutions for.

Categories: MEF, MS | Top Of Page | 13 Comments » |

13 Responses to “MEF/Lifetime: NonShared support”

josh Says:

hmm.. I’m still pondering the first point of not supporting NonShared creation in V1. With Windsor, I tend to use NonShared/Factory more often than singleton. My design style is to avoid singletons and statics except when necessary.

I suppose if I had to pick one, I’d prefer the Composition Service approach for resolving “late-bound” dependencies. I like the constructor based design because that’s the injection method I prefer (again, as in Windsor). However, I want to have dependencies resolved for me after I’ve set configuration via code or some config file. It lends itself to more flexible design and better testability, imo.

Ayende Rahien Says:

I don’t like the first approach, because it breaks the notion of DI. The factory is tightly coupled for the dependency.

And the second suffer from several problems as well. Mainly, it breaks ctor injection, and I am going to assume that CreateAttributedPart is going to be a performance problem.
That is not necessarily the case, but it did bring up warning flags.

Beyond that, those are specific approaches. If I want to use something like controller in MVC app, I would have to create one for each.

Victor Kornov Says:

The code you show with factories is boilerplate. It’s no fun to repeat it by hand all over the place.

Can’t you take a 80/20 approach on NonShared components, i.e. do what you can easily do & back off for more involved things?

Eric Nicholson Says:

I like the first example. It’s very explicit, and I don’t see why having a factory tightly coupled to the implementation is a problem. And if it is a problem, wouldn’t it be better solved with Unity or Windsor?

If the MEF is more for plugin/component oriented systems it seems like satisfying the dependencies for the instances created by a factory might be a little unwieldy.

I’m not crazy about the second approach (calling CompositionService.SatisfyImports), it seems a little magical and difficult to discover. I doesn’t really save much code anyway.

Neil Mosafi Says:

Not keen on the SatisyImports statement, I think it makes the code look messier and adds more unnecessary code to test in all your classes. Also what if the constructor has dependencies?

I’m not sure exactly how MEF works, but isn’t it possible to supply a callback to MEF when Foo is registered (to be called when MEF tries to instantiate the dependency). Or make it like a service locator as in

var foo = CompositionService.Compose();

Neil Mosafi Says:

Last example was supposed to say

var foo = CompositionService.Compose<Foo>();

hammett Says:

But guys, maybe the question should be: are you OK with us removing the Non-Shared support?

Victor Kornov Says:

Using factories all over the place for NonShared is labourous => no fun, no pit of success. Then, most people will use Shared(singleton).

Do you think Shared is a prevailing scenario for MEF? If not, then you are kind of telling your end users: “You should do NonShared. But you are on your own. Beause we only support Shared. Which you don’t want to use.”

Personally, I beleive NonShared is preferred (all other things being equal) => MEF should CLEARLY support it in NON edge cases.

josh Says:

To answer Hammett, I’m not really liking the idea of removing support for NonShared. I don’t know if perhaps I’m in the minority, but I only use Singleton, perhaps, 10-20% of the time. Probably closer to 10%; on some projects not at all.

From my perspective, I’d be inclined to not use a product where I have to work around a core principle of it 80-90% of the time. That means extra, unnecessary work for me.

Nick Aceves Says:

I would definitely be turned off to MEF if support for NonShared is dropped.

I normally use Windsor, and I use transient instances almost 100% of the time. Singleton instances are only used when absolutely necessary. In general, I find that maintaining stateless components is a VERY good thing. Having a framework that directly supports transient instances is an absolute requirement for me.

hammett Says:

Nick, if you have stateless components, why not to use singleton?

rhinof Says:

Hammett, most of the usage from my ends up being 80% transitive 20% singleton.
Since stateless components are sometimes hard to achieve I tend to use the DI framework in order to “cheat” and enforce “statelessness” by using transitive bindings when resolving dependencies.
a composition framework that hasn’t got transitive behavior would be unappealing for me.

jbe Says:

Hammett, I believe that the support for “non-shared” components is a “must-have” feature for the first release.

I wouldn’t mind to loose some of the other advanced features MEF provides (e.g. recomposition) but not this one.

jbe

Leave a Reply