it-swarm.com.de

So integrieren Sie MEF mit ASP.NET MVC 4 und ASP.NET Web API

Wie kann man Managed Extensibility Framework (MEF) mit ASP.NET MVC 4 und ASP.NET Web API in ein und demselben Projekt integrieren?

Betrachten Sie eine Beispielanwendung mit einem MVC-Controller HomeController und einem Web-API-Controller ContactController. Beide haben eine Eigenschaft vom Typ IContactRepository, die zur Auflösung von MEF abhängig ist. Das Problem ist, wie man MEF in MVC und Web API einbindet, so dass Instanzen über MEF erstellt werden.

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();
    }
}

IContactRepository und ContactRepository:

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"}
        };
    }
}

Kontakt:

public class Contact
{
    public int Id { get; set; }
    public string Name { get; set; }
}
28
aknuds1

Die Lösung besteht darin, System.Web.Mvc.IDependencyResolver und System.Web.Http.Dependencies.IDependencyResolver zu implementieren und Ihre Implementierung mit ASP.NET MVC bzw. ASP.NET Web API in Ihrem Application_Start zu registrieren. Methode.

In diesem Beispiel erstellen wir eine Klasse MefConfig, die eine Methode RegisterMef implementiert, die von Application_Start aufgerufen wird, um unseren Abhängigkeitsauflöser zu installieren. Die Klasse MefDependencyResolver implementiert sowohl System.Web.Mvc.IDependencyResolver als auch System.Web.Http.Dependencies.IDependencyResolver und handhabt als solche Abhängigkeiten für die Auflösungsaufgaben sowohl für MVC als auch für Web-API.

Application_Start, füge dies in Global.asax.cs ein:

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

MefDependencyResolver und 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
aknuds1

@ aknuds1 Antwort ist das Beste, was ich bisher für die Integration von MEF in den DependencyResolver gesehen habe. Ich konnte es erweitern, um die auf Konventionen basierende Zusammensetzung in MEF2 relativ leicht zu verwenden. Die MefConfig-Klasse ist alles, was Sie ändern müssen, und dann noch lange nicht.

/// <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
Robb Vandaveer

Sie können einen Blick darauf werfen http://kennytordeur.blogspot.be/2012/08/mef-in-aspnet-mvc-4-and-webapi.html . Es wird erläutert, wie MEF in einem Asp.net MVC 4/Web Api-Projekt verwendet wird. Es gibt auch ein Nuget-Paket , das auf diesem Code basiert. Auf diese Weise können Sie es sehr einfach und schnell testen.

1
Kenny Tordeur

Die Lösung von @ aknuds1 funktioniert, aber bei jedem API-Aufruf geht Speicher verloren. Ich habe seine Lösung geändert, um das Leck zu beheben.

public class MefDependencyResolver : System.Web.Http.Dependencies.IDependencyResolver, System.Web.Mvc.IDependencyResolver
{
    private readonly CompositionContainer container;
    private readonly List<Lazy<object, object>> exports = new List<Lazy<object, object>>();
    private readonly object syncRoot = new object();

    public MefDependencyResolver(CompositionContainer container)
    {
        this.container = container;
    }

    public IDependencyScope BeginScope()
    {
        return new MefDependencyResolver(container);
    }

    /// <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(nameof(serviceType));

        var serviceExport = container.GetExports(serviceType, null, null).FirstOrDefault();
        if (serviceExport == null) return null;

        lock (this.syncRoot)
        {
            exports.Add(serviceExport);
        }

        return serviceExport.Value;

    }

    /// <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(nameof(serviceType));

        var serviceExports = container.GetExports(serviceType, null, null);
        if (!serviceExports.Any()) return Enumerable.Empty<object>();

        lock (this.syncRoot)
        {
            exports.AddRange(serviceExports);
        }

        return serviceExports.Select(x => x.Value);
    }

    public void Dispose()
    {
        lock (this.syncRoot)
        {
            foreach (var e in exports)
            {
                this.container.ReleaseExport(e);
            }

            exports.Clear();
        }
    }
}
0
Adi Lester

Dies ist ein einfacherer Ansatz, den ich in meinem MVC4-Projekt verwende. 

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);
   }        
}

Führen Sie in Application_Start MefConfig.Initialise () und in FilterConfig.RegisterGlobalFilters (GlobalFilterCollection-Filter) Filter aus.Add (neue Filters.MefFilterAttribute ());

0
Stuntbeaver

Ich folgte der Antwort von @akanuds1, musste aber auch die ControllerFactory folgendermaßen ändern:

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
BrunoLM

Die Lösung von Herrn Kenny Torduer funktionierte für mich, während die vermeintlich richtige Antwort nicht der Fall war (die Controller-Instanz konnte nicht aufgelöst werden, obwohl alle abhängigen Teile im Catelog enthalten sind, mir wurde der Fehler "Typ hat keinen Standardkonstruktor" gegeben)!

Korrektur : Beide Ansätze funktionieren eigentlich, ich war durch einen elementaren Fehler in der Convention Parts Registry dumm. Meine aufrichtige Entschuldigung an den Autor der richtigen Antwort.

0
binjiezhao