如何将 MEF 与 MVC 4 和 ASP.NET Web API 集成 ASP.NET



如何在同一个项目中将托管扩展性框架(MEF)与 ASP.NET MVC 4和 ASP.NET Web API集成?

考虑一个示例应用程序,其中包含 MVC 控制器HomeController和 Web API 控制器ContactController。两者都有一个类型 IContactRepository 的属性,它们依靠 MEF 来解析。问题是如何将 MEF 插入 MVC 和 Web API,以便通过 MEF 创建实例。

主控制器:

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

联系人控制器:

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

联系:

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

解决方案是实现 System.Web.Mvc.IDependencyResolver 和 System.Web.Http.Dependencies.IDependencyResolver,并在 Application_Start 方法中分别向 ASP.NET MVC 和 ASP.NET Web API 注册实现。

在此示例中,我们将创建一个类MefConfig ,它实现从Application_Start调用的方法RegisterMef以安装我们的依赖项解析器。类MefDependencyResolver实现System.Web.Mvc.IDependencyResolverSystem.Web.Http.Dependencies.IDependencyResolver,因此处理 MVC 和 Web API 的依赖项解析职责。

Application_Start,把它放在你的global.asax.cs:

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

MefDependencyResolver 和 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;
    }
}

@aknuds1答案是我迄今为止见过的将 MEF 集成到依赖项解析器中的最佳答案。我能够相当轻松地扩展它以使用MEF2中基于约定的合成。MefConfig 类是所有需要更改的,然后不会更改太多。

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

你可以看看这个 http://kennytordeur.blogspot.be/2012/08/mef-in-aspnet-mvc-4-and-webapi.html。它解释了如何在 Asp.net MVC 4/Web Api项目中使用MEF。还存在基于此代码的 Nuget 包。这样您就可以非常轻松快速地对其进行测试。

@aknuds1的解决方案有效,但在每次 API 调用时都会泄漏内存。我已经修改了他的解决方案以修复泄漏。

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

这是我在 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);
   }        
}

在Application_Start运行MefConfig.Initialise()并在FilterConfig.RegisterGlobalFilters(GlobalFilterCollection过滤器)中放置过滤器。Add(new Filters.MefFilterAttribute());

我遵循了@akanuds1的回答,但我也必须将控制器工厂更改为:

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

最新更新