I’m working on an application using MEF (specifically, MEF 2 Preview 5), and I’ve run into a problem trying to import based on a generic interfaces.
I have an interface:
public interface IMessageHandler<in T>
{
void HandleMessage(T message);
}
where T is a type of message to be handled. I’m importing these things into a catalog using the RegistrationBuilder:
RegistrationBuilder context = new RegistrationBuilder();
context.ForTypesDerivedFrom(typeof(IMessageHandler<>))
.Export(builder => builder.AsContractType(typeof(IMessageHandler<>)));
and then, in the consuming class I’m using [ImportMany] to import a list of these into an IEnumerable<Lazy>>:
[ImportMany(typeof(IMessageHandler<>))]
IEnumerable<Lazy<IMessageHandler<object>, HandledMessageTypeAttribute>> _messageHandlers;
Now, here lies the first problem – you’re forced to assign a type to the generic interface at this point. I’m using Lazy<T, TMetadata> as the IMessageHandler<T> implementations have relevant metadata I want to consume (HandledMessageTypeAttribute).
Now, when I want to access any element in the IEnumerable<Lazy<>> collection I get the following exception:
Cannot cast the underlying exported value of type
'MessageHandlerImplementation (ContractName="IMessageHandler(System.Object)")'
to type 'IMessageHandler`1[System.Object]'.
I understand (roughly) why I’m getting the exception, the problem is I have no idea how to get around it. So, basically what I want to do is:
- Have a bunch of classes which implement an
IMessageHandler<T>interface. - Have them discovered at runtime with MEF.
- Import them into a collection which allows me to consume any metadata they have.
- Be able to instantiate them.
I understand I could simply make IMessageHandler non-generic and have IMessageHandler.HandleMessage() accept a parameter of type object but I was looking for a slightly more elegant solution
Any pointers or guidance is appreciated.
I don’t see a much better way to do what you’re trying to achieve without using a non-generic interface. The root of the problem is the interface’s definition:
which means that if we have two classes,
AandB, whereBderives fromA, then this is allowedbut this is not:
You’re essentially trying to perform the latter, which is what causes the exception to be thrown. I assume that what you’d like to do is to be able to get a handler, given a type. If that’s the case then you should probably use a non-generic interface and have the message type available in the export metadata. Then you’ll have something like this:
You may find this question relevant as well. Hope this helps.