Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MEF and exporting based on Metadata

OK I'm pretty sure this is something blindingly obvious but I'm not finding it.

I'm trying to export a object from MEF container based on it's Metadata.

I've seen this done in tutorials like this one:

http://blog.maartenballiauw.be/post/2009/04/21/ASPNET-MVC-and-the-Managed-Extensibility-Framework-%28MEF%29.aspx

However my export doesn't have the Metadata property that is necessary for this to work. What could be the problem there?

[Export(typeof(IController))]
[ExportMetadata("controllerName","Home")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class HomeController : Controller{}

and again

public class MyControllerFactory : IControllerFactory
{
    private readonly CompositionContainer _container;

    public MyControllerFactory(CompositionContainer container)
    {
        _container = container;
    }

    public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
    {
        var controllerExport = _container.GetExports<IController>().
                Where(exp => exp.Metadata) //Here it doesn't have the Metadata property.
    }
}

I understand that the GetExports returns a collection of Lazy that do of course not have the Metadata property but it's assumed in most tutorials I look through.

How do I do this correctly?

EDIT

This is what I did:

 public interface IControllerMetaData
{
    string Name { get; }
    string Subdomain { get; }
}

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class ControllerMetadataAttribute : ExportAttribute
{
    public ControllerMetadataAttribute(string name, string subdomain)
        : base(typeof(IControllerMetaData))
    {
        Name = name;
        Subdomain = subdomain;
    }

    public string Name { get; set; }
    public string Subdomain { get; set; }
}

Then in each controller

[Export(typeof(IController))]
[ControllerMetadata("Home", "")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class HomeController : SubdomainManagedController

and in the factory

var controllerExport = _container.GetExports<IController, ControllerMetadataAttribute>().
                                    Where(exp => exp.Metadata.Name.Equals(controllerName) && exp.Metadata.Subdomain.Equals(subdomain)).
                                    FirstOrDefault();

and I'm getting

The Type 'ControllerMetadataAttribute' supplied is not a valid Metadata View.`

How is it not valid. It has the MetaDataAttribute and all?

like image 278
Ingó Vals Avatar asked Oct 05 '11 14:10

Ingó Vals


People also ask

Is MEF still supported?

Managed Extensibility Framework (MEF) - . NET Framework | Microsoft Learn. This browser is no longer supported. Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.

Is MEF supported in .NET core?

You can use MEF in your client applications, whether they use Windows Forms, WPF, or any other technology, or in server applications that use ASP.NET. MEF has been ported as Microsoft. Composition to . NET Core as well but partially.

What is ImportingConstructor?

By using [ImportingConstructor] , you allow one class that serves as an export to import its dependencies. This dramatically simplifies the architecture, as you can decouple the dependencies of a concrete object from its implementation.


1 Answers

In your example, you're using GetExports<T>, instead of GetExports<T,TMetadata>. In a simple example, you can use GetExports<IController, IDictionary<string, object>>, which would allow you to query, but a nicer way of doing it is to create a custom metadata contract:

public interface INameMetadata
{
    string Name { get; }
}

Which you can then use as:

[Export(typeof(IController))]
[ExportMetadata("Name", "Home")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class HomeController : Controller { }

And then change your import to:

var controller = _container.GetExports<IController, INameMetadata>()
                           .Where(e => e.Metadata.Name.Equals(controllerName))
                           .Select(e => e.Value)
                           .FirstOrDefault();

Going one step further, you could combine your Export and ExportMetadata attributes into a single attribute:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false), MetadataAttribute]
public class ExportControllerAttribute : ExportAttribute, INameMetadata
{
    public ExportControllerAttribute(string name)
        : base(typeof(IController))
    {
        Name = name;
    }

    public string Name { get; private set; }
}

Now, you can use that with your export:

[ExportController("Home"), PartCreationPolicy(CreationPolicy.NonShared)]
public class HomeController : Controller { }
like image 155
Matthew Abbott Avatar answered Oct 06 '22 00:10

Matthew Abbott