Comment intégrer MEF avec ASP.NET MVC 4 et ASP.NET API Web

Comment intégrer Cadre De Gestion De L'Extensibilité (MEF) avec ASP.NET MVC 4 et ASP.NET API Web dans le même projet?

prenons un exemple d'application, avec un contrôleur MVC HomeController et un contrôleur D'API Web ContactController. Les deux ont une propriété de type IContactRepository, qu'ils comptent sur le MEF pour résoudre. Le problème est de brancher MEF dans MVC et API Web, de sorte que les instances soient créées via MEF.

HomeController:

/// <summary>
/// Home controller. Instruct MEF to create one instance of this class per importer,
/// since this is what MVC expects.
/// </summary>
[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class HomeController : Controller
{
    [Import]
    private IContactRepository _contactRepository = null;

    public ActionResult Index()
    {
        return View(_contactRepository.GetAllContacts());
    }
}

ContactController:

/// <summary>
/// Contact API controller. Instruct MEF to create one instance of this class per importer,
/// since this is what Web API expects.
/// </summary>
[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class ContactController : ApiController
{
    [Import]
    private IContactRepository _contactRepo = null;

    public Contact[] Get()
    {
        return _contactRepo.GetAllContacts();
    }
}

Icontactreprository and Contactreprository:

public interface IContactRepository
{
    Contact[] GetAllContacts();
}

[Export(typeof(IContactRepository))]
public class ContactRepository : IContactRepository
{
    public Contact[] GetAllContacts()
    {
        return new Contact[] {
            new Contact { Id = 1, Name = "Glenn Beck"},
            new Contact { Id = 2, Name = "Bill O'Riley"}
        };
    }
}

Contact:

public class Contact
{
    public int Id { get; set; }
    public string Name { get; set; }
}
28
demandé sur raistlin0788 2012-11-26 18:17:57

6 réponses

la solution est d'implémenter Système.Web.Mvc.IDependencyResolver et Système.Web.Http.Dépendance.IDependencyResolver et enregistrez votre implémentation avec ASP.NET MVC et ASP.NET API Web respectivement, dans votre Application_Start méthode.

Dans cet exemple, nous allons créer une classe MefConfig, qui implémente une méthode RegisterMef qui est appelée à partir de Application_Start afin d'installer notre résolveur de dépendances. La classe MefDependencyResolver implémente les deux System.Web.Mvc.IDependencyResolver et System.Web.Http.Dependencies.IDependencyResolver et, à ce titre, s'occupe des fonctions de résolution de dépendances pour MVC et API Web.

Application_Start, mettez ceci dans votre Global.asax.cs:

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        [...]
        MefConfig.RegisterMef();
    }
}

MefDependencyResolver and MefConfig:

/// <summary>
/// Resolve dependencies for MVC / Web API using MEF.
/// </summary>
public class MefDependencyResolver : System.Web.Http.Dependencies.IDependencyResolver, System.Web.Mvc.IDependencyResolver
{
    private readonly CompositionContainer _container;

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

    public IDependencyScope BeginScope()
    {
        return this;
    }

    /// <summary>
    /// Called to request a service implementation.
    /// 
    /// Here we call upon MEF to instantiate implementations of dependencies.
    /// </summary>
    /// <param name="serviceType">Type of service requested.</param>
    /// <returns>Service implementation or null.</returns>
    public object GetService(Type serviceType)
    {
        if (serviceType == null)
            throw new ArgumentNullException("serviceType");

        var name = AttributedModelServices.GetContractName(serviceType);
        var export = _container.GetExportedValueOrDefault<object>(name);
        return export;
    }

    /// <summary>
    /// Called to request service implementations.
    /// 
    /// Here we call upon MEF to instantiate implementations of dependencies.
    /// </summary>
    /// <param name="serviceType">Type of service requested.</param>
    /// <returns>Service implementations.</returns>
    public IEnumerable<object> GetServices(Type serviceType)
    {
        if (serviceType == null)
            throw new ArgumentNullException("serviceType");

        var exports = _container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
        return exports;
    }

    public void Dispose()
    {
    }
}

public static class MefConfig
{
    public static void RegisterMef()
    {
        var asmCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
        var container = new CompositionContainer(asmCatalog);
        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;
    }
}
28
répondu aknuds1 2012-11-26 14:17:57

Vous pouvez prendre un coup d'oeil à ce http://kennytordeur.blogspot.be/2012/08/mef-in-aspnet-mvc-4-and-webapi.html. Il explique comment utiliser le MEF dans un Asp.net MVC 4/Api Web du projet. Il existe aussi Nuget package basé sur ce code. De cette façon, vous pouvez le tester très facilement et rapidement.

1
répondu Kenny Tordeur 2012-12-12 18:04:01

@aknuds1 réponse est le meilleur que j'ai vu jusqu'à présent pour l'intégration de la MEF dans le DependencyResolver. J'ai pu l'étendre assez facilement pour utiliser la composition basée sur la convention dans MEF2. La classe MefConfig est tout ce qu'il fallait changer et puis pas de beaucoup.

/// <summary>
///     Responsible for configuring MEF for the application.
/// </summary>
public static class MefConfig
{
    /// <summary>
    ///     Registers MEF conventions and exports.
    /// </summary>
    public static void RegisterMef()
    {
        // Register MVC/API conventions
        var registrationBuilder = new RegistrationBuilder();
        registrationBuilder.ForTypesDerivedFrom<Controller>().SetCreationPolicy(CreationPolicy.NonShared).Export();
        registrationBuilder.ForTypesDerivedFrom<ApiController>().SetCreationPolicy(CreationPolicy.NonShared).Export();
        var assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly(), registrationBuilder);
        var aggregateCatalog = new AggregateCatalog(assemblyCatalog);
        var container = new CompositionContainer(aggregateCatalog);
        var resolver = new MefDependencyResolver(container);
        // Install MEF dependency resolver for MVC
        DependencyResolver.SetResolver(resolver);
        // Install MEF dependency resolver for Web API
        GlobalConfiguration.Configuration.DependencyResolver = resolver;
    }
}
1
répondu Robb Vandaveer 2013-10-11 20:17:11

la solution de M. Kenny Torduer a fonctionné pour moi alors que la réponse supposée correcte n'a pas fonctionné (je n'ai pas pu résoudre l'instance du contrôleur bien que toutes les parties dépendantes soient dans le catelog, on m'a donné une erreur "type does not have a default constructor")!

Correction: les deux approches fonctionnent en fait, j'étais stupide par une erreur élémentaire dans le registre des parties de convention. Mes sincères excuses à l'auteur de la bonne réponse.

0
répondu binjiezhao 2013-05-18 16:27:50

c'est une approche plus simple que j'utilise dans mon projet MVC4.

public static class MefConfig
{
     public static CompositionContainer MefContainer = null;

     public static void Initialise()
     {
          AggregateCatalog cat = new AggregateCatalog();
          cat.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
          MefContainer = new CompositionContainer(cat);
      }
}

public class MefFilterAttribute : ActionFilterAttribute
{
   public override void OnActionExecuting(ActionExecutingContext filterContext)
   {
      MefConfig.MefContainer.ComposeParts(filterContext.Controller);
   }        
}

Dans Application_Start exécuter MefConfig.Initialise () et dans FilterConfig.RegisterGlobalFilters (GlobalFilterCollection filters) mettre des filtres.Ajouter (nouveaux filtres.MefFilterAttribute ());

0
répondu Stuntbeaver 2013-06-13 15:58:01

j'ai suivi la réponse de @akanuds1 mais j'ai aussi dû changer le ControllerFactory en ceci:

public class MefControllerFactory : DefaultControllerFactory
{
    private readonly CompositionContainer compositionContainer;

    public MefControllerFactory(CompositionContainer compositionContainer)
    {
        this.compositionContainer = compositionContainer;
    }

    protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
    {
        var export = compositionContainer.GetExports(controllerType, null, null).SingleOrDefault();

        IController result;

        if (null != export)
        {
            result = export.Value as IController;
        }
        else
        {
            result = base.GetControllerInstance(requestContext, controllerType);
            compositionContainer.ComposeParts(result);
        }

        return result;
    }
}

Glogal.asax.cs

protected void Application_Start()
{
    ...
    var container = MefConfig.Register();
    ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(container));
}
0
répondu BrunoLM 2014-09-18 02:31:07