Community Day 2011 - Fun with ASP.NET MVC, MEF and NuGet

Edit on GitHub

To start the blog post: AWESOME! That’s what I have to say about the latest edition of Community Day 2011. I had the privilege of doing a session on ASP.NET MVC 3, MEF and NuGet, and as promised to the audience: here are the slides. For those who want to see the session, the recording can be found on Channel 9 from a previous event.

“Fun with ASP.NET MVC3, MEF and NuGet”
Community Day 2011, Mechelen, Belgium, 23/06/2011

Abstract: “So you have a team of developers… And a nice architecture to build on… How about making that architecture easy for everyone and getting developers up to speed quickly? Learn all about integrating the managed extensibility framework (MEF) and ASP.NET MVC with some NuGet sauce for creating loosely coupled, easy to use architectures that anyone can grasp.

Here’s the slides:

Here’s the example code: Fun with ASP.NET MVC 3 MEF - CommunityDay 2011.zip (6.79 mb)

Some resources:

This is an imported post. It was imported from my old blog using an automated tool and may contain formatting errors and/or broken images.

Leave a Comment

avatar

7 responses

  1. Avatar for Jan
    Jan July 19th, 2011

    Hi,

    Ik heb wat gespeeld met je demo applicatie. Maar er is iets zeer bizar, iets wat ik niet kan vatten. In het project Initech.Components.Web.Mvc.Authentication zitten de views voor de Account controller. Als ik de account views gewoon uit het project haal blijft alles mooi werken. Als ik dan een nieuwe view maak en deze probeer aan te roepen vanuit de controller, dan komt er steeds de error dat de view niet is te vinden. Kan jij mij hier verder mee helpen wat hier het probleem is of hoe dit alles werkt ?

    Bedankt

  2. Avatar for Dadv
    Dadv July 27th, 2011

    Bonjour,

    J&#39ai suivi avec attention votre intervention au MIX 11 et j&#39ai voulu essayer certaines choses avec les sources fournies.

    En premier lieu je cherchais &#224 faire en sorte que MefContrib puisse charger une dll autre part que dans bin, j&#39ai donc modifi&#233 le fichier AppStart_MefContribMVC3.cs comme suit :

    public static class AppStart_MefContribMVC3
    {
    public static void Start()
    {
    // Register the CompositionContainerLifetimeHttpModule HttpModule.
    // This makes sure everything is cleaned up correctly after each request.
    CompositionContainerLifetimeHttpModule.Register();

    // Create MEF catalog based on the contents of ~/bin.
    //
    // Note that any class in the referenced assemblies implementing in "IController"
    // is automatically exported to MEF. There is no need for explicit [Export] attributes
    // on ASP.NET MVC controllers. When implementing multiple constructors ensure that
    // there is one constructor marked with the [ImportingConstructor] attribute.
    var catalog = new AggregateCatalog(
    [b]new DirectoryCatalog(System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "Plugins")),[/b]
    new DirectoryCatalog("bin"),
    new ConventionCatalog(new MvcApplicationRegistry())); // Note: add your own (convention)catalogs here if needed.

    // Tell MVC3 to use MEF as its dependency resolver.
    var dependencyResolver = new CompositionDependencyResolver(catalog);
    DependencyResolver.SetResolver(dependencyResolver);

    // Tell MVC3 to resolve dependencies in controllers
    ControllerBuilder.Current.SetControllerFactory(
    new DefaultControllerFactory(
    new CompositionControllerActivator(dependencyResolver)));

    // Tell MVC3 to resolve dependencies in filters
    FilterProviders.Providers.Remove(FilterProviders.Providers.Single(f => f is FilterAttributeFilterProvider));
    FilterProviders.Providers.Add(new CompositionFilterAttributeFilterProvider(dependencyResolver));

    // Tell MVC3 to resolve dependencies in model validators
    ModelValidatorProviders.Providers.Remove(ModelValidatorProviders.Providers.OfType<DataAnnotationsModelValidatorProvider>().Single());
    ModelValidatorProviders.Providers.Add(
    new CompositionDataAnnotationsModelValidatorProvider(dependencyResolver));

    // Tell MVC3 to resolve model binders through MEF. Note that a model binder should be decorated
    // with [ModelBinderExport].
    ModelBinderProviders.BinderProviders.Add(
    new CompositionModelBinderProvider(dependencyResolver));
    }
    }

    Sans avoir le succ&#232s escompt&#233.

    Je me suis donc orient&#233 vers une impl&#233mentation de PartRegistry :

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Web;
    using MefContrib.Hosting.Conventions.Configuration;
    using MefContrib.Hosting.Conventions;
    using System.Web.Mvc;
    using MefContrib.Web.Mvc;

    namespace MefLocalTests.Codes
    {
    public class PluginMvcApplicationRegistry : PartRegistry
    {
    /// <summary>
    /// Initializes a new instance of the <see cref="MvcApplicationRegistry"/> class.
    /// </summary>
    public PluginMvcApplicationRegistry()
    {
    Scan(x =>
    {
    x.Assembly(Assembly.GetExecutingAssembly());
    x.Directory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins"));
    x.Directory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
    });

    Part()
    .ForTypesAssignableFrom<IController>()
    .MakeNonShared()
    .AddMetadata(new PartCreationScopeAttribute(PartCreationScope.Default))
    .ExportAs<IController>()
    .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().Where(mbr => mbr.GetCustomAttributes(typeof(ImportAttribute), false).Length > 0).ToArray());
    });
    }
    }
    }

    et

    public static class AppStart_MefContribMVC3
    {
    public static void Start()
    {
    // Register the CompositionContainerLifetimeHttpModule HttpModule.
    // This makes sure everything is cleaned up correctly after each request.
    CompositionContainerLifetimeHttpModule.Register();

    // Create MEF catalog based on the contents of ~/bin.
    //
    // Note that any class in the referenced assemblies implementing in "IController"
    // is automatically exported to MEF. There is no need for explicit [Export] attributes
    // on ASP.NET MVC controllers. When implementing multiple constructors ensure that
    // there is one constructor marked with the [ImportingConstructor] attribute.
    var catalog = new AggregateCatalog(
    new DirectoryCatalog("bin"),
    new ConventionCatalog([b]new PluginMvcApplicationRegistry()[/b])); // Note: add your own (convention)catalogs here if needed.

    // Tell MVC3 to use MEF as its dependency resolver.
    var dependencyResolver = new CompositionDependencyResolver(catalog);
    DependencyResolver.SetResolver(dependencyResolver);

    // Tell MVC3 to resolve dependencies in controllers
    ControllerBuilder.Current.SetControllerFactory(
    new DefaultControllerFactory(
    new CompositionControllerActivator(dependencyResolver)));

    // Tell MVC3 to resolve dependencies in filters
    FilterProviders.Providers.Remove(FilterProviders.Providers.Single(f => f is FilterAttributeFilterProvider));
    FilterProviders.Providers.Add(new CompositionFilterAttributeFilterProvider(dependencyResolver));

    // Tell MVC3 to resolve dependencies in model validators
    ModelValidatorProviders.Providers.Remove(ModelValidatorProviders.Providers.OfType<DataAnnotationsModelValidatorProvider>().Single());
    ModelValidatorProviders.Providers.Add(
    new CompositionDataAnnotationsModelValidatorProvider(dependencyResolver));

    // Tell MVC3 to resolve model binders through MEF. Note that a model binder should be decorated
    // with [ModelBinderExport].
    ModelBinderProviders.BinderProviders.Add(
    new CompositionModelBinderProvider(dependencyResolver));
    }
    }

    Mais l&#224 encore, aucun succ&#232s.

    Par contre si je met ma dll dans le bin, tout fonctionne parfaitement.

    Pouvez vous m&#39indiquer la marche &#224 suivre pour externaliser le dossier contenant les dll d&#39export svp ?

    D&#39avance merci,

    Dadv

    ----

    Hi,

    I want to make MefContrib load a external Dll in an other directory than bin, so i changed the AppStart_MefContribMVC3.cs file :

    public static class AppStart_MefContribMVC3
    {
    public static void Start()
    {
    // Register the CompositionContainerLifetimeHttpModule HttpModule.
    // This makes sure everything is cleaned up correctly after each request.
    CompositionContainerLifetimeHttpModule.Register();

    // Create MEF catalog based on the contents of ~/bin.
    //
    // Note that any class in the referenced assemblies implementing in "IController"
    // is automatically exported to MEF. There is no need for explicit [Export] attributes
    // on ASP.NET MVC controllers. When implementing multiple constructors ensure that
    // there is one constructor marked with the [ImportingConstructor] attribute.
    var catalog = new AggregateCatalog(
    [b] new DirectoryCatalog(System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "Plugins")),[/b]
    new DirectoryCatalog("bin"),
    new ConventionCatalog(new MvcApplicationRegistry())); // Note: add your own (convention)catalogs here if needed.

    // Tell MVC3 to use MEF as its dependency resolver.
    var dependencyResolver = new CompositionDependencyResolver(catalog);
    DependencyResolver.SetResolver(dependencyResolver);

    // Tell MVC3 to resolve dependencies in controllers
    ControllerBuilder.Current.SetControllerFactory(
    new DefaultControllerFactory(
    new CompositionControllerActivator(dependencyResolver)));

    // Tell MVC3 to resolve dependencies in filters
    FilterProviders.Providers.Remove(FilterProviders.Providers.Single(f => f is FilterAttributeFilterProvider));
    FilterProviders.Providers.Add(new CompositionFilterAttributeFilterProvider(dependencyResolver));

    // Tell MVC3 to resolve dependencies in model validators
    ModelValidatorProviders.Providers.Remove(ModelValidatorProviders.Providers.OfType<DataAnnotationsModelValidatorProvider>().Single());
    ModelValidatorProviders.Providers.Add(
    new CompositionDataAnnotationsModelValidatorProvider(dependencyResolver));

    // Tell MVC3 to resolve model binders through MEF. Note that a model binder should be decorated
    // with [ModelBinderExport].
    ModelBinderProviders.BinderProviders.Add(
    new CompositionModelBinderProvider(dependencyResolver));
    }
    }

    No success

    So i try to implement a PartRegistry :

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Web;
    using MefContrib.Hosting.Conventions.Configuration;
    using MefContrib.Hosting.Conventions;
    using System.Web.Mvc;
    using MefContrib.Web.Mvc;

    namespace MefLocalTests.Codes
    {
    public class PluginMvcApplicationRegistry : PartRegistry
    {
    /// <summary>
    /// Initializes a new instance of the <see cref="MvcApplicationRegistry"/> class.
    /// </summary>
    public PluginMvcApplicationRegistry()
    {
    Scan(x =>
    {
    x.Assembly(Assembly.GetExecutingAssembly());
    x.Directory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins"));
    x.Directory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
    });

    Part()
    .ForTypesAssignableFrom<IController>()
    .MakeNonShared()
    .AddMetadata(new PartCreationScopeAttribute(PartCreationScope.Default))
    .ExportAs<IController>()
    .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().Where(mbr => mbr.GetCustomAttributes(typeof(ImportAttribute), false).Length > 0).ToArray());
    });
    }
    }
    }

    And

    public static class AppStart_MefContribMVC3
    {
    public static void Start()
    {
    // Register the CompositionContainerLifetimeHttpModule HttpModule.
    // This makes sure everything is cleaned up correctly after each request.
    CompositionContainerLifetimeHttpModule.Register();

    // Create MEF catalog based on the contents of ~/bin.
    //
    // Note that any class in the referenced assemblies implementing in "IController"
    // is automatically exported to MEF. There is no need for explicit [Export] attributes
    // on ASP.NET MVC controllers. When implementing multiple constructors ensure that
    // there is one constructor marked with the [ImportingConstructor] attribute.
    var catalog = new AggregateCatalog(
    new DirectoryCatalog("bin"),
    new ConventionCatalog([b]new PluginMvcApplicationRegistry()[/b])); // Note: add your own (convention)catalogs here if needed.

    // Tell MVC3 to use MEF as its dependency resolver.
    var dependencyResolver = new CompositionDependencyResolver(catalog);
    DependencyResolver.SetResolver(dependencyResolver);

    // Tell MVC3 to resolve dependencies in controllers
    ControllerBuilder.Current.SetControllerFactory(
    new DefaultControllerFactory(
    new CompositionControllerActivator(dependencyResolver)));

    // Tell MVC3 to resolve dependencies in filters
    FilterProviders.Providers.Remove(FilterProviders.Providers.Single(f => f is FilterAttributeFilterProvider));
    FilterProviders.Providers.Add(new CompositionFilterAttributeFilterProvider(dependencyResolver));

    // Tell MVC3 to resolve dependencies in model validators
    ModelValidatorProviders.Providers.Remove(ModelValidatorProviders.Providers.OfType<DataAnnotationsModelValidatorProvider>().Single());
    ModelValidatorProviders.Providers.Add(
    new CompositionDataAnnotationsModelValidatorProvider(dependencyResolver));

    // Tell MVC3 to resolve model binders through MEF. Note that a model binder should be decorated
    // with [ModelBinderExport].
    ModelBinderProviders.BinderProviders.Add(
    new CompositionModelBinderProvider(dependencyResolver));
    }
    }

    No more success

    But if i put the Dll in the bin folder, everything ok

    Could you tell me where is my mistake?

    Thx a lot,

    Dadv

  3. Avatar for maartenba
    maartenba July 28th, 2011

    Can you try this one:

    var catalog = new AggregateCatalog(
    new DirectoryCatalog("Plugins"),
    new DirectoryCatalog("bin"),
    new ConventionCatalog(new MvcApplicationRegistry())); // Note: add your own (convention)catalogs here if needed.

  4. Avatar for maartenba
    maartenba July 28th, 2011

    Mail me anders even

  5. Avatar for Dadv
    Dadv July 28th, 2011

    thx for the answers,

    but it don&#39t work any more :(

    If i break point i can see that catalog contain my external controller, but it&#39s look like it don&#39t load it...

    I can send you a test project if need.

    Dadv

  6. Avatar for Dadv
    Dadv July 29th, 2011

    Hi again,

    I just look in the MefContrib.Mvc3 source code... everywhere you fond bin directory hard-coded :

    public MvcApplicationRegistry()
    {
    base.Scan(delegate(ITypeScannerConfigurator x)
    {
    x.Assembly(Assembly.GetExecutingAssembly());
    [b]x.Directory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));[/b]
    }
    );
    base.Part().ForTypesAssignableFrom<IController>().MakeNonShared().AddMetadata(new PartCreationScopeAttribute(PartCreationScope.Default)).ExportAs<IController>().Export().Imports(delegate(IImportRegistry x)
    {
    x.Import().Members(delegate(Type m)
    {
    ConstructorInfo[] array = new ConstructorInfo[1];
    array[0] = (m.GetConstructors().FirstOrDefault((ConstructorInfo c) => c.GetCustomAttributes(typeof(ImportingConstructorAttribute), false).Length > 0) ?? m.GetGreediestConstructor());
    return array;
    }
    );
    x.Import().Members((Type m) => (
    from mbr in m.GetMembers()
    where mbr.GetCustomAttributes(typeof(ImportAttribute), false).Length > 0
    select mbr).ToArray<MemberInfo>());
    }
    );
    }

    ///

    public static void Bootstrap()
    {
    AggregateCatalog catalog = new AggregateCatalog(new ComposablePartCatalog[]
    {
    [b]new DirectoryCatalog("bin"), [/b]
    new ConventionCatalog(new IPartRegistry<IContractService>[]
    {
    new MvcApplicationRegistry()
    })
    });
    CompositionDependencyResolver compositionDependencyResolver = new CompositionDependencyResolver(catalog);
    DependencyResolver.SetResolver(compositionDependencyResolver);
    ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(new CompositionControllerActivator(compositionDependencyResolver)));
    FilterProviders.Providers.Remove(FilterProviders.Providers.Single((IFilterProvider f) => f is FilterAttributeFilterProvider));
    FilterProviders.Providers.Add(new CompositionFilterAttributeFilterProvider(compositionDependencyResolver));
    ModelValidatorProviders.Providers.Remove(ModelValidatorProviders.Providers.OfType<DataAnnotationsModelValidatorProvider>().Single<DataAnnotationsModelValidatorProvider>());
    ModelValidatorProviders.Providers.Add(new CompositionDataAnnotationsModelValidatorProvider(compositionDependencyResolver));
    ModelBinderProviders.BinderProviders.Add(new CompositionModelBinderProvider(compositionDependencyResolver));
    }

    ///

    So i try to remove the bin declaration in AppStart_MefContribMVC3

    var catalog = new AggregateCatalog(
    [b] //new DirectoryCatalog("bin"),[/b]
    new ConventionCatalog(new MvcApplicationRegistry())); // Note: add your own (convention)catalogs here if needed.

    And... nothing append ... all work like previously.

    So what the use of this "new DirectoryCatalog("bin")" here?
    And why any other DirectoryCatalog are ignore ?

    My goal is simple, i want to have in a "Plugins" directory a Plugin.dll File and a Views Directory

    My Plugin.dll contain a DemoController.

    My main mvc application contain a HomeController.

    Now if i type /Home, i should see the Home View (/Views/Home/Index.cshtml)
    If i type /Demo, i should see the Demo View (/Plugins/Views/Demo/Index.cshtml)

    I know you blog about this, but your previous project where not using MefContrib.MVC3

    If you could not help me about this, can you give me a link where i can find some help please?

    Thx a lot,

    Dadv

    ps: you can email me if necessary.

  7. Avatar for maartenba
    maartenba August 2nd, 2011

    Can you send me an email on this? (note Ill be on vacation the next weeks and may be slow to respond)