Import mismatch exception, huh?

Oct 16, 2009 at 4:08 AM
Edited Oct 16, 2009 at 4:10 AM

Hi there, I'll try to keep the question simple. I have the following:

[Export]
public class MainWindowViewModel : ViewModel<MainWindow>
{
    [Import]
    public IMessageBoxService MessageBoxService { get; set; }

    [ImportingConstructor]
    public MainWindowViewModel(MainWindow view)
        : base(view)
    {
        // some stuff here ... 
    }

    // more stuff here ... 
}


/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
[Export]
public partial class MainWindow : Window, IView
{
    /// <summary>
    /// Initializes a new instance of the <see cref="MainWindow"/> class.
    /// </summary>
    public MainWindow()
    {
        InitializeComponent();            
    }
}

But when I compose it (in App.cs) and then try get the MainWindowViewModel instance:

// MEF code...
container.ComposeParts(new StandardMessageBoxService());
// more MEF code...
container.GetExportedValue<MainWindowViewModel>();

It gave me this exception:

System.ComponentModel.Composition.ImportCardinalityMismatchException was unhandled
Message="No valid exports were found that match the constraint '((exportDefinition.ContractName = \"Titanic.ViewModels.MainWindowViewModel\") && (exportDefinition.Metadata.ContainsKey(\"ExportTypeIdentity\") && \"Titanic.ViewModels.MainWindowViewModel\".Equals(exportDefinition.Metadata.get_Item(\"ExportTypeIdentity\"))))', invalid exports may have been rejected." Source="System.ComponentModel.Composition" StackTrace
at System.ComponentModel.Composition.Hosting.ExportProvider.GetExports(ImportDefinition definition, AtomicComposition atomicComposition) in C:\My Documents\Tutorials\Framework\MEF_PreBeta_2\src\ComponentModel\System\ComponentModel\Composition\Hosting\ExportProvider.cs: line 110
at System.ComponentModel.Composition.Hosting.ExportProvider.GetExportsCore(Type type, Type metadataViewType, String contractName, ImportCardinality cardinality) in C:\My Documents\Tutorials\Framework\MEF_PreBeta_2\src\ComponentModel\System\ComponentModel\Composition\Hosting\ExportProvider.GetExportOverrides.cs: line 799
at System.ComponentModel.Composition.Hosting.ExportProvider.GetExportedValueCore<T>(String contractName, ImportCardinality cardinality) in C:\My Documents\Tutorials\Framework\MEF_PreBeta_2\src\ComponentModel\System\ComponentModel\Composition\Hosting\ExportProvider.GetExportOverrides.cs: line 735

...


The reason why I do the GetExportedValue<MainWindowViewModel> is because it appears that my MainWindow isn't registering the MainWindowViewModel as the DataContext. I did the GetExportedValue to confirm and woala...

Any ideas? Thanks.

Edit: If i do it the old school way by manually wiring things up, it works fine. I must have missed something when I try to MEF-ified it.

Oct 16, 2009 at 4:43 AM
Edited Oct 16, 2009 at 4:46 AM

Never mind, I think I fixed it about 2 minutes after posting this. :|

Looks like the offending code is the one with trying to manually add a message box service instance. It should be this:

 

batch.AddExportedValue<IMessageBoxService>(new StandardMessageBoxService());

 

Which is different from the guide (http://mef.codeplex.com/wikipage?title=Hosting%20MEF%20and%20the%20Container&referringTitle=Guide)
in regards to manually adding an instance as part of the composition. Now I have some more questions:

  1. What's the difference between batch.AddExportedValue vs container.ComposeParts methods?
  2. If the exception stems from not being able to satisfy the import (which I think the exception error string needs to be a little bit clearer),
    how do I make the [Import] as optional (since if MEF can't satisfy [ImportMany], it doesn't raise an exception)
  3. If let's say I have two instances that [Export(typeof(IMessageBoxService)], how do I tell the [Import] to choose one or the other?
    a little bit like LINQ 'First' and 'FirstOrDefault' extension methods.
  4. If #3 is not possible, how do I guard/prevent external parts/plugins/addins from exporting a duplicate type (in this case, IMessageBoxService)?

Thanks for your time.

Oct 16, 2009 at 4:44 AM
Your MainWindowViewModel looks like it has been rejected due to an IMessageBox implementation being present. Can you show the code for StandardMessageBoxService?
 
MEF's stable composition feature will prevent parts from showing up whose imports cannot be satisfied, in this case the MainWindowViewModel.
 
Glenn
Oct 16, 2009 at 4:52 AM
Edited Oct 16, 2009 at 5:11 AM
gblock wrote:
Your MainWindowViewModel looks like it has been rejected due to an IMessageBox implementation being present. Can you show the code for StandardMessageBoxService?
 
MEF's stable composition feature will prevent parts from showing up whose imports cannot be satisfied, in this case the MainWindowViewModel.
 
Glenn

hi glenn, thanks for the prompt reply. I'm suprised msft guys are working this late. :-)

The StandardMessageBoxService implementation borrows heavily from Sacha's WPFMessageBoxService from the Cinch framework (http://cinch.codeplex.com/).

Edit: i.e., StandardMessageBoxService is almost identical to sacha's

 

Oct 16, 2009 at 5:02 AM

Actually instead of creating a batch you can use the ComposeExportedValue extension method which creates a batch behind the scenes.

1. The difference between ComposeParts and ComposeExportedValue is this. When you call ComposeExportedValue you are saying "Take this instance and add it to the container with this contract".

When you call ComposeParts however you are saying here is a part which has imports which need to be satisfied and Exports that should be available to other importers.

I am guessing that your MessageBox service did not have an [Export(typeof(IMessageBox))] is that correct?

2. The reason you are not seeing an exception is because there is no exception. With stable composition, the MainPartViewModel was kept from being discovered because it's import is missing. In the past the behavior would be an exception.

Check this post for more on the rationale: http://blogs.msdn.com/nblumhardt/archive/2009/07/17/light-up-or-mef-optional-exports.aspx

3. Check this post for thoughts on how to apply default behavior: http://codebetter.com/blogs/glenn.block/archive/2009/05/14/customizing-container-behavior-part-2-of-n-defaults.aspx

4. If you want to guard against duplicates there are a couple of approaches. One is you could use a concrete type and seal it, this way others can't derive from it.

Alternatively you could write a custom decorator catalog that applies a filter over an inner catalog and does not allow any IMessageBox implementations to pass through.

HTH

Glenn

Oct 16, 2009 at 5:04 AM

Forgot to mention, you can have optional imports. To do that you specify AllowDefault = True on the import. Once you do, the part will not get rejected if the import is missing.

 

Oct 16, 2009 at 5:16 AM
Edited Oct 16, 2009 at 5:18 AM

Thanks Glenn, that clarifies a lot. Yes, the StandardMsgBoxService doesn't have the export attribute since I figured I can add it manually. I'll switch it to use the attribute.

I'll read over the blog in detail tomorrow.

I have to say, once I was able to get MEF up and running, I am extremely pleased and excited. First time I've been this excited since my first 'hello world'.
Can't wait for the final release. You really have a world changing project here...

 

Oct 16, 2009 at 2:18 PM
Sure thing, glad you like it.
Keep the feedback coming :-)

Glenn
________________________________________
From: azurepalm [email removed]
Sent: Thursday, October 15, 2009 10:16 PM
To: Glenn Block
Subject: Re: Import mismatch exception, huh? [MEF:72178]

From: azurepalm

Thanks Glenn, that clarifies a lot.

I'll read over the blog in detail tomorrow.

I have to say, once I was able to get MEF up and running, I am extremely pleased and excited. First time I've been this excited since my first 'hello world'.
Can't wait for the final release. You really have a world changing project here...



Read the full discussion online<http://mef.codeplex.com/Thread/View.aspx?ThreadId=72178&ANCHOR#Post246373>.

To add a post to this discussion, reply to this email ([email removed]<mailto:[email removed]?subject=[MEF:72178]>)

To start a new discussion for this project, email [email removed]<mailto:[email removed]>

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe or change your settings<https://mef.codeplex.com/subscriptions/thread/project/edit> on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com