Coproject - a RIA Caliburn.Micro demo, part 5

by Augustin Šulc

In this part, we will add some module metadata. Note that you can get the most recent Coproject source codes from its Codeplex site.

Metadata

As you probably noticed, modules are not loaded into shell in any specified order. And since we don’t want the modules be in arbitrary order, we need to add some metadata. You can read more about MEF metadata here.

The easiest way is to use ExportMetadataAttribute. We would update HomeViewModel as:

[Export(typeof(IModule))]
[ExportMetadata("Order", 10)]
public class HomeViewModel : Screen, IModule

and the other ViewModels in the same manner. Next, must update ShellViewModel constructor. Instead of IModule directly, we will request Lazy<IModule, IModuleMetadata> from MEF. This will make MEF to provide lazy reference to requested instance and also metadata of type IModuleMetadata. You might read an introduction to Lazy here.

Let’s create IModuleMetadata in new folder /Framework/:

namespace Coproject.Framework
{
	public interface IModuleMetadata
	{
		int Order { get; }
	}
}

Finally, ShellViewModel’s constructor:

[ImportingConstructor]
public ShellViewModel([ImportMany]IEnumerable<Lazy<IModule, IModuleMetadata>> moduleHandles)
{
	var modules = from h in moduleHandles orderby h.Metadata.Order select h.Value;
	Items.AddRange(modules);
}

The code should be self-describing. Now, run the application. You may experiment with order numbers and see that the order in application menu changes respectively.

Strongly typed metadata

Although this solution works, you probably don’t like the idea of writing magic strings like “Order”. So we should make these metadata strongly typed. To do this, add new class ExportModuleAttribute to the Framework folder. Application structure should look like this:
image

Add this implementation of the created class:

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class)]
public class ExportModuleAttribute : ExportAttribute, IModuleMetadata
{
	public int Order { get; private set; }

	public ExportModuleAttribute(int order)
		: base(typeof(IModule))
	{
		Order = order;
	}
}

As you can see, we simply extend the original ExportAttribute to accept order integer. In this case, we can add default export type (IModule). The first attribute [MetadataAttribute] is very important because it tells MEF to also load metadata from ExportModuleAttribute. Finally, we can merge the two attributes on each module viewmodel:

[Export(typeof(IModule))]
[ExportMetadata("Order", 10)]

into one:

[ExportModule(10)]
public class HomeViewModel : Screen, IModule

Do this for all module view models and run the application.

PartCreationPolicy

Talking about MEF exports, you might wonder what happens when you import a component more than once – will MEF provide you the same instance or will it create a new one? The answer is: It depends on configuration. You can specify this behavior on the places. Either on export:

[ExportModule(10)]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class HomeViewModel : Screen, IModule

Or on import:

[Import(typeof(IShell), RequiredCreationPolicy=CreationPolicy.Shared)]
public IShell Shell { get; set; }

CreationPolicy enum has three options:
Shared – MEF will return always the same instance (singleton pattern)
NonShared – MEF will always create a new instance
Any – not specified, depends on the other side of contract (export/import). This is default and if not set to other, this will act as Shared by default.

You can read more about this topic here. I suggest reading whole series MEF for Beginner or MEF Programming Guide.

Tags: Caliburn, Coproject, Mef

1 Comment

  • web agency said

    Useful information shared..Iam very happy to read this article..thanks for giving us nice info.Fantastic walk-through. I appreciate this post.

Add a Comment