使用WebAPI2的MVC 5中的MEF - 最佳实践

时间:2015-09-22 15:55:45

标签: c# angularjs asp.net-mvc asp.net-web-api2 mef

我正在构建一个简单的网站,我希望能够轻松地将新闻和事件等模块插入其中,以便将来重用这些模块。经过一番挖掘后,我发现最好的方法是MEF。使用Kenny Tordeur's walkthrough我使网站处于半工作状态。 然后,我根据Long Le's Blog进行了一些更改和扩展。仍然没有快乐。我当前的问题是我的MefDependencyResolver应该提供System.Web.Http.Hosting.IHostBufferPolicySelector的一个实例,但它唯一想做的是CompositionContainer上的GetExports(),以及这个接口的实现还没有出口,这根本不起作用。

我现在感觉我完全错了。

我的网站是使用ASP.Net MVC 5构建的,此时它相当简单。我的NewsModule,我试图插入"插入"使用MEF也在MVC 5中,它使用WebAPI 2来处理一组AngularJS请求(例如获取新闻)。

更糟糕的是,模块的体系结构基于this blog - 我发现它非常有用但现在意味着模块中的API控制器具有需要以某种方式注入的依赖关系。

所以,有点代码: 我的 Global.asax 如下所示:

protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        MefConfig.RegisterMef(null);
        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
        ViewEngines.Engines.Add(new MefViewEngine(MefConfig.GetPlugins()));
    }

MefConfig.cs

public static void RegisterMef(TypeCatalog typeCatalog)
    {
        var compositionContainer = ConfigureContainer(typeCatalog);
        ServiceLocator
            .SetLocatorProvider(() => new MefServiceLocator(compositionContainer));

        ControllerBuilder
            .Current.SetControllerFactory(
            new MefControllerFactory(compositionContainer));

        GlobalConfiguration
            .Configuration
            .DependencyResolver =
            new MefDependencyResolver(compositionContainer);

        //compositionContainer.ComposeParts(); IS this not needed??
    }

    private static CompositionContainer ConfigureContainer(ComposablePartCatalog composablePartCatalog)
    {

        var catalog = new AggregateCatalog(new AssemblyCatalog(Assembly.GetExecutingAssembly()));

        foreach (var plugin in GetPlugins())
        {
            var directoryCatalog = new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules", plugin));
            catalog.Catalogs.Add(directoryCatalog);

        }

        if (composablePartCatalog != null)
            catalog.Catalogs.Add(composablePartCatalog);

        return new CompositionContainer(
            catalog,
            new MefNameValueCollectionExportProvider(
                ConfigurationManager.AppSettings));
    }

    public static List<string> GetPlugins()
    {
        var pluginFolders = new List<string>();
        var plugins = Directory.GetDirectories(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules")).ToList();
        plugins.ForEach(s =>
        {
            var di = new DirectoryInfo(s);
            pluginFolders.Add(di.Name);
        });
        return pluginFolders;
    } 

MefControllerFactory.cs

public class MefControllerFactory : DefaultControllerFactory
{
    private readonly CompositionContainer m_CompositionContainer;
    public MefControllerFactory(CompositionContainer container)
    {
        m_CompositionContainer = container;
    }

    public override IController CreateController(RequestContext requestContext, string controllerName)
    {
        var controller = GetInstance<IController>(controllerName) ??
                         GetControllerInstance(requestContext, typeof(Controller));

        return controller;
    }

    protected override IController GetControllerInstance(
    RequestContext requestContext, Type controllerType)
    {
        var name = AttributedModelServices.GetContractName(controllerType);
        var export = m_CompositionContainer
            .GetExports(controllerType, null, name).SingleOrDefault();

        IController result;

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

        return result;
    }

    public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
    {
        return SessionStateBehavior.Default;
    }

    public override void ReleaseController(IController controller)
    {
        var disposableController = controller as IDisposable;

        disposableController?.Dispose();
    }

    private T GetInstance<T>(string contractName = null)
    {
        var type = default(T);
        if (m_CompositionContainer == null) return type;

        if (string.IsNullOrEmpty(contractName) || string.IsNullOrWhiteSpace(contractName))
        {
            return m_CompositionContainer.GetExportedValue<T>();
        }

        var export = m_CompositionContainer.GetExports(typeof(T), null, contractName).SingleOrDefault();
        if (export != null)
        {
            return (T)export.Value;
        }
        return type;
    }
}

MefDependencyResolver.cs

public class MefDependencyResolver : IDependencyResolver
{
    private readonly CompositionContainer m_Container;

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

    public IDependencyScope BeginScope()
    {
        return this;
    }

    public object GetService(Type serviceType)
    {

        var name = AttributedModelServices.GetContractName(serviceType);
        var export = m_Container.GetExports(serviceType, null, name).SingleOrDefault();

        return export?.Value;
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        var name = AttributedModelServices.GetContractName(serviceType);
        var exports = m_Container.GetExports(serviceType, null, name);
        var createdObjects = new List<object>();

        var enumerable = exports as Lazy<object, object>[] ?? exports.ToArray();
        if (enumerable.Any()) createdObjects.AddRange(enumerable.Select(export => export.Value));
        return createdObjects;
    }

    public void Dispose()
    {
        ;
    }
}

新闻模块中 ArticleController 的一部分:

[Export(typeof(IController))]
[PartCreationPolicy(CreationPolicy.NonShared)]
[System.Web.Http.RoutePrefix("api/article")]
public class ArticleController : ApiController
{
    private IArticleService ArticleService { get; }
    private IAttachmentService AttachmentService { get; }

    public ArticleController(IArticleService articleService, IAttachmentService attachmentService)
    {
        ArticleService = articleService;
        AttachmentService = attachmentService;
    }


    [ResponseType(typeof(IEnumerable<ArticleListView>))]
    [System.Web.Http.HttpGet]
    public IHttpActionResult Get()
    {
        var articles = ArticleService.GetArticles();

        if (articles == null)
        {
            return NotFound();
        }
        return Ok(articles.OrderByDescending(a => a.Created).Select(a => new ArticleListView(a)));
    }

仍然在新闻模块中,我有以下文章的资源:

var ArticleService = function ($resource) {
return $resource('/api/article/:id',{id:'@id'}, {
    update:{method:'PUT'}
});

然后从Controller Article.query($ scope.articles,function(resp){...}

查询

我的问题是?

  1. 这是使用MEF的正确方法吗?
  2. 当我的模块的控制器有DI?
  3. 时,是否可以使用MEF
  4. 如果以上答案都是&#34;是&#34;那么我在MefDependencyResolver中做错了什么,最终会出现异常?
  5. 提前多多感谢

0 个答案:

没有答案