Strongly typed metadata

Dec 18, 2012 at 5:49 AM
Edited Dec 18, 2012 at 5:49 AM

Hi all,

I'm relatively new to MEF, so please forgive me for poor code or if this is blindingly obvious.

I'm using the System.Composition namespace from the MEF for web and Windows Store apps NuGet package in a new ASP.NET MVC4 project.

I've read that in MEF2 you no longer use Lazy<IExtension, IExtensionMetadata>, but now you must provide a concrete type for the metadata view and use ExportFactory<> instead of Lazy<>.

However, I can't find any examples of how this should all work - just a few mentions of using a concrete type instead of an interface.

I've tried a few things, but keep getting the following exception - "Export metadata for 'AccountID' is missing and no default value was supplied".

My code...

Creating the container (in Global.asax or App_Start folder):

// Get assemblies that will be providing imports and exports
var assemblies = GetAssemblies();

// Get conventions that will be used to find imports and exports
var conventions = GetConventions();

var container = new ContainerConfiguration().WithAssemblies(assemblies, conventions).CreateContainer();

// Create and apply a MefControllerFactory so controllers can be composed
ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(container));

GetConventions() method:

private static ConventionBuilder GetConventions()
{
    var conventionBuilder = new ConventionBuilder();
    conventionBuilder.ForTypesDerivedFrom<IController>().Export();

    conventionBuilder.ForTypesDerivedFrom<IExtension>().Export<IExtension>();
    conventionBuilder.ForTypesMatching(t => t.Namespace != null && t.Namespace.EndsWith(".Parts")).Export().ExportInterfaces();

    return conventionBuilder;
}

IExtension.cs: 

public interface IExtension
{
    void DoWork();
}

ExtensionMetadata.cs: 

public class ExtensionMetadata
{
    public int AccountID { get; set; }
}

ExtensionA.cs (same as ExtensionB.cs): 

public void DoWork()
{
    System.Diagnostics.Debug.WriteLine("ExtensionA doing work..");
}

ExtensionManager.cs: 

public class ExtensionManager
{       
    private IEnumerable<ExportFactory<IExtension, ExtensionMetadata>> _extensions;

    public ExtensionManager(IEnumerable<ExportFactory<IExtension, ExtensionMetadata>> extensions)
    {
        _extensions = extensions;
    }

    public void DoWork(int accountID)
    {
        foreach (var extension in _extensions)
        {
            if (extension.Metadata.AccountID == accountID)
            {
                extension.DoWork();
            }                   
        }           
    }
}

I think I'm missing something quite major here. Basically I want to lazily import all Extensions, check their metadata and if a condition is fulfilled have that extension do something.

Am I missing some attributes somewhere? Are they still required in MEF2?

Would really appreciate your help or any links to sample code / tutorials that cover this scenario.

I've posted this question on StackOverflow, but so far have not had any replies so am also posting here (hope that's ok?).

Many thanks!

Dec 18, 2012 at 6:24 AM
Edited Dec 18, 2012 at 6:30 AM

I think I've worked it out after reading this SO question.

I created a Metadata Attribute:

[MetadataAttribute]
public class ExtensionMetadataAttribute : ExportAttribute, IExtensionMetadata
{
	public int ID { get; set; }

	public ExtensionMetadataAttribute(int id) : base(typeof (IExtension))
	{
		ID = id;
	}
}

Then modified ExtensionA.cs:

[ExtensionMetadata(1)]
public class ExtensionA : IExtension
{
	public void DoWork()
	{
		System.Diagnostics.Debug.WriteLine("ExtensionA doing work..");
	}
}

And now ExtensionManager.cs looks like this:

public class ExtensionManager : IExtensionManager
{
	private readonly IEnumerable<ExportFactory<IExtension, ExtensionMetadata>> _extensions;

	public ExtensionManager(IEnumerable<ExportFactory<IExtension, ExtensionMetadata>> extensions)
	{
		_extensions = extensions;
	}

	public void DoWork(int extensionID)
	{
		foreach (var extension in _extensions)
		{
			if (extension.Metadata.ID == extensionID)
			{
				using (var foo = extension.CreateExport())
				{
					foo.Value.DoWork();
				}
			}
		}
	}
}

This seems to do the trick, but I would still be interested in your feed back re best practices, performance issues etc.

Thanks!