Nested containers with MEF

Topics: Prism v4 - Silverlight 4, Prism v4 - WPF 4
Mar 1, 2011 at 10:07 AM
Edited Mar 1, 2011 at 10:23 AM

Can anyone point me at a sample that shows how to create nested containers using Prism with MEF?

In my main module, I'd like to be able to define a container that provides a common set of services globally. Then in another module, I'd like to be able to create a child container that provides services locally (and only locally) to that module. Scoped containers are easy to set up in CAB and Unity.

What I do NOT want is for every service, view and view model in my app to end up in the same container where anyone can get hold of them. Unfortunately, this is exactly what you get as soon as you do this:

// In main module...
this.Container.ComposeExportedValue<CompositionContainer>(this.Container);

// In ModuleA
[ModuleExport(typeof(ModuleA))]
public class ModuleA : IModule
{
  [ImportingConstructor]
  public ModuleA(CompositionContainer parentContainer)
  {
    // too late! - container catalog has already been populated with Module A exports
  }

Should I even try to use MEF here or go with something else?

 

Developer
Mar 1, 2011 at 3:47 PM
Edited Mar 1, 2011 at 3:48 PM

Hi,

You might find the MEF FAQ in Codeplex useful, specifically into the section titled "How do I use Nested Containers?".

Also, as this is not strictly related to Prism, you might find better support in the MEF forums:

I hope you find this helpful.

Guido Leandro Maliandi
http://blogs.southworks.net/gmaliandi

Mar 1, 2011 at 6:16 PM

Thanks for the reply.

Unfortunately, that link doesn't really help. I already understand how to create nested containers in MEF.

What I'm looking for is an explanation or code sample of how to have a Prism module (other than the module that creates the default container) create a MEF child container on a parent container. Without this, you end up with everything in a single container.

Developer
Mar 2, 2011 at 1:49 PM

Hi,

Based on my understanding, one possible workaround for that would be to declare the exports imperatively instead of using the declarative Export attribute. Therefore, you would guarantee that the parts are exported into the child container instead of the parent one. You might find the following articles useful to that purpose:

Take into account that the ModuleExport attribute, needed to load modules, loads them (along with the child containers) into the parent container, so you shouldn't replace it by an imperative alternative.

If you don't find the imperative use of MEF comfortable, you could use other dependency injection mechanisms, such as Unity, which is supported by Prism out of the box.

I hope you find this helpful.

Guido Leandro Maliandi
http://blogs.southworks.net/gmaliandi

Mar 3, 2011 at 7:15 PM

OK, so if I go the imperative route, I have to make sure I do NOT have [Export] on the types I want only in the child container.

But then they're not exports, so how can I add them to a child container?

This sort of thing

[ImportingConstructor]
public ModuleA(CompositionContainer parent)
{
    this.container =
        new CompositionContainer(
            new TypeCatalog(
                typeof(ModuleAView),
                typeof(ModuleAViewModel)),
        parent);
}

won't work because ModuleAView and ModuleAViewModel are not exports any more.

And it still leaves me with this problem when it comes to registering a view:

public void Initialize()
{
    // The region view registry knows nothing about the child container so this fails
    this.RegionViewRegistry.RegisterViewWithRegion(RegionNames.Main, typeof(ModuleAView));
}

 

Apr 8, 2011 at 8:57 PM
Edited Apr 8, 2011 at 9:00 PM

Hi,

You can create your own IModuleInitializer that uses a child container to resolve instances of your modules.

I had to do something similar with the UnityContainer.  Here is the code:

 

    public class NestedModuleInitializer : ModuleInitializer
    {
        private IUnityContainer _unityContainer;

        public NestedModuleInitializer(IUnityContainer unityContainer, ILoggerFacade loggerFacade)
            : base(new UnityServiceLocatorAdapter(unityContainer), loggerFacade)
        {
            _unityContainer = unityContainer;
        }

        protected override IModule CreateModule(string typeName)
        {
            Type moduleType = Type.GetType(typeName);
            if (moduleType == null)
            {
                throw new ModuleInitializeException(string.Format(CultureInfo.CurrentCulture, Properties.Resources.FailedToGetType, typeName));
            }

            IUnityContainer childContainer = _unityContainer.CreateChildContainer();
            object module = childContainer.Resolve(moduleType);
            return (IModule)module;
        }
    }

Cheers,

Andrew Michell

Apr 12, 2011 at 7:52 AM

Hi,

Thanks for the reply.

I guess that's one way to make it work but as I wrote earlier, I was really looking for a way to have a Prism module (other than the module that creates the default container) create a MEF child container on a parent container, and populate it with services that are then available only to parts within the child container. (Otherwise you end up with everything in one big bucket.)

This is really easy to do in CAB and Unity.

I could use Unity, but then I've got two DI frameworks to deal with. OK, those frameworks may have slightly different goals (as Glenn has pointed out many many many times) but they do have a lot of overlap, and that worries me. If you're setting out on a large project with different developers in different timezones, you want everybody working on that project to be completely clear about precisely what responsibilities they're going to hand off to each technology. Right now, it looks to me like you can't really use MEF for container management inside Prism unless you've got only a simple app (which, given you're using Prism in the first place, you probably haven't). I'd like to be wrong about that but I haven't come across any code yet which proves otherwise.

(I've got a feeling you could do something clever with export providers but that's the sort of thing I'd like to see in the core, not something I want to go off and write myself.)