So we've got MEF working with MVC4, how about the convention based model?
Asked Answered
G

2

3

After much digging about I figured it's possible to use MEF for DI in MVC4, below SO link gives a couple of examples:

How to integrate MEF with ASP.NET MVC 4 and ASP.NET Web API

They work fine, but I'm wondering how to eliminate the need of explicitly "Import" and "Export" the obvious like your controllers in the MVC 4 app?

There are some suggestions on the web, some top programming minds' blogs. But I had little success in replicating their success stories. Just to name a couple:

Any suggestions please?

Glauconite answered 18/5, 2013 at 16:40 Comment(0)
G
0

Ok, I have a solution now.

The MEFcontrib offers good support for the convention based model. So simply NuGet it(install-package mefcontrib) to your mvc 4 project.

Once you have the mefcontrib binaries, you just need to register the conventions first and then harness it by adding the convention catalogue. Below snippet shows how:

Convention registration:

public class InitPartsConvention : PartRegistry
{
    public InitPartsConvention()
    {
        Scan(x => x.Assembly(Assembly.GetExecutingAssembly()));

        Part()
            .ForTypesAssignableFrom<IHttpController>()
            .MakeNonShared()
            .Export()
            .Imports(x =>
            {
                x.Import().Members(
                    m => new[] {
                                 m.GetConstructors()
                                 .FirstOrDefault(
                                    c => c.GetCustomAttributes(typeof(ImportingConstructorAttribute), false).Length > 0) 
                                    ?? m.GetGreediestConstructor()
                             });

                x.Import().Members(
                    m => m.GetMembers(BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic)
                        .Where(
                        mbr => mbr.GetCustomAttributes(typeof(ImportAttribute), false).Length > 0).ToArray()
                        );
            });

        Part()
            .ForTypesAssignableFrom<IController>()
            .MakeNonShared()
            .Export()
            .Imports(x =>
            {
                x.Import().Members(
                    m => new[] {
                                 m.GetConstructors()
                                 .FirstOrDefault(
                                    c => c.GetCustomAttributes(typeof(ImportingConstructorAttribute), false).Length > 0) 
                                    ?? m.GetGreediestConstructor()
                             });

                x.Import().Members(
                    m => m.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Where(
                        mbr => mbr.GetCustomAttributes(typeof(ImportAttribute), false).Length > 0).ToArray()
                        );
            });
    }
}

This registers your api controllers and mvc controller so that they can be MEFified. It does not cover the AsyncController however.

MEF MVC bootstrapper

public static class MefBootstrapper
{
    public static void RegisterMef()
    {
        var container = GetContainer();
        var resolver = new MefDependencyResolver(container);
        // Install MEF dependency resolver for MVC
        DependencyResolver.SetResolver(resolver);

        // Install MEF dependency resolver for Web API
        System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver 
            = resolver;
    }

    private static CompositionContainer GetContainer()
    {
        var path = HostingEnvironment.MapPath("~/bin");
        if (path == null) throw new Exception("Unable to find the path");

        var catelog = new AggregateCatalog(
            new DirectoryCatalog(path),
            new ConventionCatalog(new InitPartsConvention()));  // this adds the convention to MEF
        return new CompositionContainer(catelog);
    }
}

That's it, job done! Enjoy dynamic DI in your MVC 4 apps.

Glauconite answered 22/5, 2013 at 11:28 Comment(2)
throws "The type MEF.MVC4.MefDependencyResolver does not appear to implement Microsoft.Practices.ServiceLocation.IServiceLocator. Parameter name: commonServiceLocator"Spencerianism
You can Install-Package MefContrib.MVC3 and get registration for free. Plus a new attribute, PartCreationScope. Pretty handy.Impressive
P
1

The version that ships with .NET 4.0 has no built in way to do this. I think MEF 2 ships with 4.5 and it has some more options including naming conventions. There is probably a version you can download and use with .NET 4.0 somewhere on NuGet.

Microsofts continuous development and pre-releases of their open source libraries is great, but its sometimes quite hard to work out what features are available in each release and what version has made it into which framework. I can't really find a definitive answer...

Pneumatology answered 20/5, 2013 at 0:4 Comment(1)
Well, that's certainly an option although not quite what I'm looking for. But thanks.Glauconite
G
0

Ok, I have a solution now.

The MEFcontrib offers good support for the convention based model. So simply NuGet it(install-package mefcontrib) to your mvc 4 project.

Once you have the mefcontrib binaries, you just need to register the conventions first and then harness it by adding the convention catalogue. Below snippet shows how:

Convention registration:

public class InitPartsConvention : PartRegistry
{
    public InitPartsConvention()
    {
        Scan(x => x.Assembly(Assembly.GetExecutingAssembly()));

        Part()
            .ForTypesAssignableFrom<IHttpController>()
            .MakeNonShared()
            .Export()
            .Imports(x =>
            {
                x.Import().Members(
                    m => new[] {
                                 m.GetConstructors()
                                 .FirstOrDefault(
                                    c => c.GetCustomAttributes(typeof(ImportingConstructorAttribute), false).Length > 0) 
                                    ?? m.GetGreediestConstructor()
                             });

                x.Import().Members(
                    m => m.GetMembers(BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic)
                        .Where(
                        mbr => mbr.GetCustomAttributes(typeof(ImportAttribute), false).Length > 0).ToArray()
                        );
            });

        Part()
            .ForTypesAssignableFrom<IController>()
            .MakeNonShared()
            .Export()
            .Imports(x =>
            {
                x.Import().Members(
                    m => new[] {
                                 m.GetConstructors()
                                 .FirstOrDefault(
                                    c => c.GetCustomAttributes(typeof(ImportingConstructorAttribute), false).Length > 0) 
                                    ?? m.GetGreediestConstructor()
                             });

                x.Import().Members(
                    m => m.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Where(
                        mbr => mbr.GetCustomAttributes(typeof(ImportAttribute), false).Length > 0).ToArray()
                        );
            });
    }
}

This registers your api controllers and mvc controller so that they can be MEFified. It does not cover the AsyncController however.

MEF MVC bootstrapper

public static class MefBootstrapper
{
    public static void RegisterMef()
    {
        var container = GetContainer();
        var resolver = new MefDependencyResolver(container);
        // Install MEF dependency resolver for MVC
        DependencyResolver.SetResolver(resolver);

        // Install MEF dependency resolver for Web API
        System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver 
            = resolver;
    }

    private static CompositionContainer GetContainer()
    {
        var path = HostingEnvironment.MapPath("~/bin");
        if (path == null) throw new Exception("Unable to find the path");

        var catelog = new AggregateCatalog(
            new DirectoryCatalog(path),
            new ConventionCatalog(new InitPartsConvention()));  // this adds the convention to MEF
        return new CompositionContainer(catelog);
    }
}

That's it, job done! Enjoy dynamic DI in your MVC 4 apps.

Glauconite answered 22/5, 2013 at 11:28 Comment(2)
throws "The type MEF.MVC4.MefDependencyResolver does not appear to implement Microsoft.Practices.ServiceLocation.IServiceLocator. Parameter name: commonServiceLocator"Spencerianism
You can Install-Package MefContrib.MVC3 and get registration for free. Plus a new attribute, PartCreationScope. Pretty handy.Impressive

© 2022 - 2024 — McMap. All rights reserved.