Visual Studio Extensibility and MefedMVVM

 

Initially the project started out as a quick hack together to enable a single piece of functionality. However as functionality is growing its time for the codebase to grow up too, and this includes the UI elements. I’ve decided to use MefedMVVM for its design time blend features and easy to grok codebase.

If you try and use it out of the box with your MEF Component and debug, you might get the following error:

System.ComponentModel.Composition.ImportCardinalityMismatchException occurred
  Message=No valid exports were found that match the constraint '(((exportDefinition.ContractName == "RunOrDebugViewModel") etc…

Doh! Taking a look at the default runtime IComposer (what tells the container where to look for your assemblies) we can understand a little more of why it can’t find our assemblies.

private AggregateCatalog GetCatalog()
{
    var catalog = new AggregateCatalog();
    var baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
    var extensionPath = String.Format(@"{0}\Extensions\", baseDirectory);
    catalog.Catalogs.Add(new DirectoryCatalog(baseDirectory));
    catalog.Catalogs.Add(new DirectoryCatalog(baseDirectory, "*.exe"));
    if (Directory.Exists(extensionPath))
        catalog.Catalogs.Add(new DirectoryCatalog(extensionPath));
    return catalog;

}

It’s basing the location from the current AppDomain, which makes perfect sense in a normal application, and infact should work with a deployed VSIX.

However when debugging a VSIX it resolves to:

"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\Extensions\"

Odd, looking there my assemblies aren’t deployed there at all, debugging to find out where they actually are I can see:

“C:/Users/naeem.khedarun/AppData/Local/Microsoft/VisualStudio/10.0Exp/Extensions/Naeem Khedarun/NBehave/0.5.0.0/NBehave.VS2010.Plugin.Editor.dll”

I see, it’s actually deployed into the experimental instance of visual studio. Luckily for us, MefedMVVM does have a mechanism for us to override the default discovery logic.

So the first thing to do, is at whichever the first visual studio provider initialises first, we need to configure the MefedMVVM bootstrapper:

LocatorBootstrapper.ApplyComposer(new VisualStudioRuntimeComposer());

 

This will override the default runtime composer with our own, so lets implement the IComposer interface and tell it to look in the appropriate place…

public class VisualStudioRuntimeComposer : IComposer
{
    public ComposablePartCatalog InitializeContainer()
    {
        return GetCatalog();
    }

    public IEnumerable<ExportProvider> GetCustomExportProviders()
    {
        return null;
    }

    private AggregateCatalog GetCatalog()
    {
        var location = (from assembly in AppDomain.CurrentDomain.GetAssemblies()
                             where assembly == typeof (ServiceRegistrar).Assembly
                             select assembly.Location).First();

        var directory = Path.GetDirectoryName(location);

        var catalog = new AggregateCatalog();
        catalog.Catalogs.Add(new DirectoryCatalog(directory));

        return catalog;
    }
}

 

We look in the current AppDomain as before, but this time look for our loaded assembly (one should be loaded or the bootstrapper wouldn’t have been called) and grabs its directory.

We can now resolve ViewModels in the experimental instance! I haven’t yet tried the new Composer with the normal instance, however if there are any issues I’ll make a follow up post.

blog comments powered by Disqus