Composite Programming Model?

Mar 17, 2009 at 3:04 PM
Hi,

I'm a newbe to MEF and just starting to understand how it works. So if this is a silly idea, we'll just skip it ;-).

Anyway, I was reading about how Imports and Exports are matched using 3 types of info: Contract Name, Type and 'other' metadata.

Now I'm thinking in a composite (WPF) application context here and I am looking for a way to load application Modules through MEF and control what an application Module sees in terms of services and regions. So I want a Module Context that is sort of a sandbox for a Module.

The current programming model uses code attributes to express the import and exports. That is perfect for code-related things like the Type. Perhaps also the contract name is applicable here (although not sure how one should use that yet). But other metadata is perhaps somewhat too 'hard-coded' to put that in a code attribute?

I thought that perhaps I could use that metadata to control what Module gets access to what services (in context of the Module). But then that information should not be specified in code attributes because it is specific to the Host (because Modules can be reused across multiple Hosts). So a config or Xaml source would be more appropriate for that information. But it has to 'merge' with the information in the code attributes. So that comes to two programming models acting as one. Hence the title.

I hope I haven't lost you here, but do you have any thoughts on this?

Thanx,
Marc Jacobi
Mar 18, 2009 at 6:09 PM

Hi Marc,

 

This is definitely an interesting scenario for us, and not at all a silly idea J. We’ve discussed things along similar lines but don’t expect to address this out-of-the-box in the near future.

 

There are some ‘alternative programming model’-type features in MEF Contrib. You might find that project to be a good place to investigate this further…

 

Thanks for your input!

Nick

Mar 18, 2009 at 6:18 PM
Marc, you may also want to look into using scoped containers in a hierarchy for providing this context. MEF allows you to create child, and parent containers, thus you can create a module specific container that injects module specific components which is a child of a global container. Each container can have it's own specific catalog, for example.

var rootCatalog = ...
var rootContainer = new CompositionContainer(rootCatalog);

var moduleACatalog = ...
var moduleAAContainer = new CompositionContainer(moduleACatalog, rootContainer);

var moduleBCatalog = ...
var moduleBContainer = new CompositionContainer(moduleBCatalog, rootContainer);

You would need to customize the module catalog service in Prism to support creating and configuring the module container.

HTH
Glenn
Mar 19, 2009 at 12:46 AM

Marc,

The approach suggested by Glenn is a very powerful way to do what I think you want to do. I am using that method myself for an application that requires module contexts. In the class where I create new modules (based on a user requesting one), I have the following code:

 p

rivate CompositionContainer CreateMeasurementContainer()
{
   var filteredCatalog = new FilteredCatalog(_rootContainer.Catalog, exp => exp.Metadata.ContainsKey(_MODULE_SCOPE) && exp.Metadata[_MODULE_SCOPE].Equals(true)); 
    CompositionContainer container = new CompositionContainer(filteredCatalog, _rootContainer);
    CompositionBatch batch = new CompositionBatch();
    batch.AddExportedObject(
"MeasurementContainer", container);
    batch.AddPart(
this);
    container.Compose(batch);
    return container;
}

...
 public IModule Create<T>()
{
    CompositionContainer container = CreateMeasurementContainer();
    IModule mod = container.GetExportedObject<IModule>();
...

 

 

To make this work, you need to implement a filtered export provider (Glenn has one on one of his blog postings) and decorate your module-scoped parts with the appropriate metadata. The alternative would be to completely separate the implementations of the module-scoped parts into a different assembly. Either way, once you hook the parent and child containers together like this, anything you pull from the module scoped container has full access to all module scoped parts as well as any parts in the root (parent) container. Even better yet, the cardinality of the parts is relative to the container from which it is pulled, so you can have multiple modules in existence even if the module-scoped parts need to be singletons wrt. the module (i.e. any aggregated parts served by the child container can easily import a handle to "their" module or other singleton module-scoped parts).

This feature has to be one of the most potent, under-advertised features in MEF!
Mar 19, 2009 at 9:09 AM
Edited Mar 19, 2009 at 9:21 AM

@Nick: I'm glad I'm not silly ;-) What! Not in the next release!? :-P
I'll see if I can fix this problem in 'user code' first before writing my own programming model. But thanks for the tip.

@Glenn: Thank you!! I was trying to do exactly what you showed in your code but was doing it way too complicated, with a CatalogExportProvider that pointed to the catalog of the root container. But the CompositionContainer IS an ExportProvider. That is exactly what I need.
I haven't looked into how I'm going to integrate MEF into CAL yet. My initial idea was skipping the ModuleCatalog completly (because I have no idea how I can extract ModuleInfo from MEF exports without loading them, but that might change ;-). The ModuleManager would simply implement a Module collection Import and MEF will do the rest.

@vandermeulen: (are you dutch? ;-) Thanx for the additional explaination. This is comming very close to what I need. If I understand your code correctly (I've read the Guide online that showed the FilteredCatalog), all modules get the exact same content in their container - everything that is tagged with _MODULE_SCOPE. What would the point of creating a separate container for each module then (other than cardinality)? Unless you change the filtering for each module container?

I need to be able to inject specific (versions of) services in specific module contexts. So I might (for instance) have some config in my host that defines "module profiles". Specifications on what services a specific module gets (or explicitly dont get). I have a default profile (and perhaps a guest profile too) that contains the context-contents for unnamed or unrecognized modules and specific profiles that are linked to specific modules. So If moduleX loads it gets a specific version of a logger service (for instance) and should not get the default logger service other modules get or the one from the rootContainer.

A lot of new info to play with. Thank you everyone.
Marc

Mar 19, 2009 at 3:34 PM
Marc,

You might want to read this blog post. This post talks about how easy it was for me to put a rather complex filtering mechanism in place. Your code could access configuration information here. I assume you could access that configuration through MEF parts.

In the same GetExports method, I see no reason you could not add to the metadata if that is helpful in your context. This specifically allows you to add information to the part based on configuration which is later accessible without loading the part. For example, in my column in the April Visual Studio Magazine, I use metadata to supply menu information for parts which avoids early loading of the parts themselves.

There are other ways to extend MEF, but I suggest that you start with the simplest that might work (which I think in your case is an aggregate catalog override such as in my blog post) and if that is to limiting, explain the additional considerations.

I trust that if I am giving you bad advice here, Glenn, Nick, Wes, Andreas or someone else will jump in.

Kathleen
Mar 19, 2009 at 5:05 PM
Marc

The filtered catalog code is on our wiki, and can be found here.

http://mef.codeplex.com/Wiki/View.aspx?title=Filtering%20Catalogs