I just hunted down an problem in my mef application; problem was, that I had an [Import] instead of [ImportMany] in my IEnumerable<IFoo> property. I started to wonder why. MEF sees that the injection target is a “collection” and could determine that collection is needed instead of a single element. At least Ninject works this way.
Does anyone have insight why [ImportMany] is required? Only reason I can think of is that one might want to [Export(typeof(IEnumerable<IBar>)] public IEnumerable<Bar> { get; } but is this really the reason for this design? I bet I’m not the only one who has been debugging this kind of error.
It’s not the same 😉
[Import]indicates that you want to import a single thing according to a contract. In MEF, a contract is just a string, and when you import a type (likeIEnumerable<IBar>), you’re really importing according to a contract which is just the name of that type.In MEF, cardinality is very important, so when you state that you wish to import a single instance of something that fits the stated contract, there can only be a single source. If multiple exports are found, an exception is thrown because of cardinality mismatch.
The
[Import]functionality doesn’t contain special logic to handleIEnumerable<T>, so from its perspective, it’s just a contract like everything else.The
[ImportMany]attribute, however, exists especially to bridge that gap. It accepts zero to any number of exports for the stated contract. This means that instead of having a single export ofIEnumerable<IBar>you can have many exports ofIBarscattered across multiple assemblies, and there’s never going to be a cardinality mismatch.In the end it’s a design philosphy. MEF could have had special, built-in knowledge about
IEnumerable<T>. Autofac (and apparently Ninject) does that and call it a Relationship Type.However, special-casing like that implies that somewhere the implementing code violates the Liskov Substitution Principle, which again can lead to POLA violations, so in this case I tend towards taking side with the MEF designers. Going for a more explicit API may decrease discoverability, but may be a bit safer.