Exports and the Cartesian product

Aug 1, 2011 at 9:50 AM
Edited Aug 1, 2011 at 9:51 AM

I'd like to produce the Cartesian product between exports, illustrated here by this example:

interface IInfoObject;
interface IProcessor;

[Export(typeof(IProcessor))]
class InfoObjectProcessor : IProcessor
{
    [ImportingConstructor]
    public InfoObjectProcessor([Import] IInfoObject info);
}

[Export(typeof(Engine))]
class Engine
{
    [ImportingConstructor]
    Engine([ImportMany] IEnumerable<IProcessor> processors);
}

When I query my catalog for the Engine part, I'd like all of the IInfoObjects in the container to be wrapped by the InfoObjectProcessor and create many IProcessors that can satisfy the Engine export. Here, this makes sense because there's an importing constructor that can create many IProcessors given an IInfoObject.

But this doesn't work and instead, if I have anything but a single IInfoObject export I get the cardinality issue and no processors.

This is the first time I find myself having to do this and maybe it's the result of doing something malicious to begin with. I'd like to know how this problem is best solved, I haven't written custom ExportProviders but it seems like I should be doing that, creating an export provider that would provide an IProcessor export for each IInfoObject export. I guess the easiest way is just to create a companion processor export for each info export but that's a lot of labor that I'd like to avoid, if reasonably possible.

 

John

Aug 1, 2011 at 10:18 AM
Edited Aug 1, 2011 at 10:19 AM

A very simple solution, and the one I'm going with right now is to just import IInfoObjects along side processors in the Engine constructor and since Engine is an export itself, this is sort of extensible as well.

[Export(typeof(Engine))]
class Engine
{
    [ImportingConstructor]
    Engine([ImportMany] IEnumerable<IProcessor> processors, [ImportMany] IEnumerable<IInfoObject> infos)
    {
        Processor = processors.Concat(from info in infos select new InfoObjectProcessor(info));
    }
}
Aug 1, 2011 at 7:11 PM

Sounds like what you is essentially an adaptation-style behavior, where one - of many- parts "coverts" all exports of contract X to contract Y. MEF doesn't support this behavior; we discussed this scenario before, and decided against supporting it, as it dramatically increases the complexity of the composition in some cases.

You solution is ceratinly a viable one; and it can be made further extensible is needed(e.g instead of directly creating InfoObjectProcessor, you may, for example, import IInfoObjectProcessor or IInfoObjectProcessorFactory. Assuming, of course, that you requite this level of extensibility)

Hope this helps

Oleg

Aug 2, 2011 at 11:52 AM
Edited Aug 2, 2011 at 11:53 AM

Thanks Oleg,

I should probably move InfoObjectProcessor into the Engine class because it's really a private implementation detail. And since the [ImportMany] IEnumerable<IInfoObject> parameter is defined the way it is, it's completely optional (stable composition won't break my Engine) and shouldn't get in the way if you at a later point decide to swap out the implementation of Engine.

It will do nicely.

John