Dave
Dave

Reputation: 15016

A new MEF error I've not seen before -- "The export is not assignable to type..."

I was very surprised to get this error today, as it's one that I've never encountered before. Everything in the code looked okay, so I did some searches. The previous questions and their respective answers didn't help.


This one was solved when the poster made sure his assembly references were consistent. I don't have this issue right now because I'm currently referencing another project in my solution.

This one was solved when the poster was instructed to use ImportMany, but I am already using it (I think properly, too) to try to load multiple plugins

This one was solved when the poster realized that there was a platform target mismatch. I've already gone through my projects to ensure that everything targets x86.


So here's what I am trying to do. I have a plugin that owns a connection to a device. I might also need to be able to share that connection with another plugin. I decided that the cleanest way to do this was to create an interface that would allow the slave plugin to request its own connection to the device. Let's just call it IConnectionSharer. If the slave plugin does not need to borrow this connection and has its own, then it should use its own implementation of IConnectionSharer to connect to the device.

My "master" plugin (the one that owns the connection to the device) implements IConnectionSharer. It also exports this via ExportAttribute.

My "slave" plugin assembly defines a class that also implements and exports IConnectionSharer.

When the application loads, the intent is for my slave plugin, via MEF, to enumerate all IConnectionSharers and store them in an IEnumerable<IConnectionSharer>. It does so like this:

[ImportMany]
public IEnumerable<IConnectionSharer> AllSharedConnections { get; set; }

Nicholas asked me to show my Exports, so here they are.

"Master" plugin:

[PartCreationPolicy(CreationPolicy.Shared)]
[Export(typeof(Interface1))]
[Export(typeof(Interface2))]
[Export(typeof(Interface3))]
[Export(typeof(IConnectionSharer))]
public partial class MasterPlugin : Interface1, Interface2, Interface3, IConnectionSharer
{
    ...
}

"Slave" plugin:

[PartCreationPolicy(CreationPolicy.Shared)]
[Export(typeof(Interface1))]
public class SlavePlugin : Interface1
{
    private Model _model { get; set; }
    private ViewModel _viewmodel { get; set; }

    [ImportingConstructor]
    public SlavePlugin( [Import] Model model) 
    {
        _model = model;
        _viewmodel = new ViewModel( model);
    }
}

Model:

[Export(typeof(Model))]
public class Model
{
    [ImportMany(typeof(IConnectionSharer))]
    private IEnumerable<IConnectionSharer> AllSharedConnections { get; set; }
    ...
}

IConnectionSharer implementation internal to "slave" plugin:

[Export(typeof(IConnectionSharer))]
public class PrivateConnection : IConnectionSharer
{
    ...
}

But during part composition, I get the error the export 'Company.MasterPlugin (ContractName="IConnectionSharer")' is not assignable to type 'IConnectionSharer'.

The error message itself seems clear enough -- it's as if MEF thinks my master plugin doesn't inherit from IConnectionSharer... but it does! Can anyone suggest further debugging strategies? I'm going to start the painful process of single stepping through the MEF source.

UPDATE

This is an interesting clue -- if, after clearing out my output folder and rebuilding the solution, I delete the "master" plugin (so that the "slave" plugin will use its own IConnectionSharer object), my application loads just fine, and my slave plugin also behaves as expected. If I put the master plugin back into the plugins folder, I get the MEF composition problem again.

I also thought I'd try to use Lazy<> instantiation to see if that had any effect. The result was a little startling. MEF complains with this error: Cannot populate the collection 'AllSharedConnections' because it does not implement ICollection or is read-only. If the collection is not IEnumerable<T> or T[] it must implement ICollection and be either pre-initialized or be writable with a default constructor.

HUH? Clearly AllSharedConnections is an IEnumerable<T>, so why is MEF complaining?

Upvotes: 2

Views: 2710

Answers (4)

Dave
Dave

Reputation: 15016

I figured out what was causing my problem, but I'm not sure of the best way to solve it. I'll post the solution now, but will then post a new question because it doesn't really have anything to do with MEF.

As stated before, I have a device that connects to the PC via a dongle. I want to be able to share that dongle with another device, but the 3rd party software for the dongle does not allow simultaneous connections to it.

My intent was to allow "slave" plugins to "borrow" the connection from "master" connections, and do this via an interface that I call IConnectionSharer. The good news is that I've confirmed that it works -- I just had to figure out my MEF error.

Interfacing with the dongle is achieved by calling their C DLL with a C# DLL (which uses a C# wrapper class to call into the C DLL via PInvoke). However, in reality I have two of these dongles, but the 3rd party library doesn't allow two on a computer. I was able to trick it into working by creating another copy of my C# DLL wrapper as a completely separate assembly. This assembly simply had links to the .cs files in my other C# DLL, but had a different copy of their C# wrapper class. I modified this copy to call into a different copy of their C DLL, to force it to load at a different address in the process memory space. Up until now, it has worked perfectly for us, without any communication problems whatsoever over years of use.

Now a UML diagram is probably necessary to point out the source of the problem:

alt text

As you can see, one "master" plugin will have a connection to one dongle via DongleConnection, while the other "master" plugin will have a connection to the other dongle via DongleConnection2. Both classes are in the same namespace. The "slave" plugin will need to communicate via one dongle or the other via IConnectionSharer. It gets access to a "feature" by calling RequestFeature().

The problem is that DongleConnection and DongleConnection2 each are associated with IFeature, which is defined in each of their respective assemblies. When I added IConnectionSharer, that interface was also in each of these assemblies. My application loads both master plugins, so when the slave plugin loads, there is ambiguity at that point of which IFeature is getting requested by the slave plugin.

At least this is my understanding of the problem. I would appreciate any comments if you think I am misleading anyone with my analysis. After figuring this out, it turns out that Wim has the exact answer (I just saw his edit after posting this answer, so I had to change whom I awarded the answer to!). I just didn't think about the source of the duplicate contract assembly because the magical details of the DongleConnection and DongleConnection2 classes had been long forgotten. :)

I can currently make this all work now, but my solution isn't optimal, so I'll have to post a different question regarding other possible solutions.

Upvotes: 1

Wim Coenen
Wim Coenen

Reputation: 66733

You most likely have an old version of an assembly lying around in your output folder, e.g. after a rename. Normally this is not a problem because no other assemblies reference this old one. With MEF however, assemblies can be picked up for the composition just by being present in the application folder.

Clean up your output folder, rebuild and see if the problem goes away.

edit: since the problem doesn't go away when you clean up your output folder, my next guess would be that you have two declarations of IConnectionSharer, or you are compiling the same code into two different assemblies. This will result in two IConnectionSharer interfaces with the same name but a different identity.

Upvotes: 4

Randy Minder
Randy Minder

Reputation: 48422

Is it possible you have two different versions of your contract assembly, and each are getting loaded? One could be from an executable path and the other from a plugin path.

These might help:

http://blogs.msdn.com/b/dsplaisted/archive/2010/07/13/how-to-debug-and-diagnose-mef-failures.aspx

http://msdn.microsoft.com/en-us/library/dd153782.aspx

Upvotes: 0

Wes Haggard
Wes Haggard

Reputation: 4364

Out of curiousity check to see if your assembly that contains the type IConnectionSharer is loaded twice under the debugger. It is possible for the assembly to be loaded in multiple contexts in the process and in that case two types that should be the same would appear to be different if they referenced from a different version of the assembly that is loaded.

Upvotes: 1

Related Questions