【问题标题】:MEF and ASP.NET MVCMEF 和 ASP.NET MVC
【发布时间】:2011-03-03 07:11:03
【问题描述】:

我想将 MEF 与 asp.net mvc 一起使用。 我写了以下控制器工厂:

public class MefControllerFactory : DefaultControllerFactory
{
    private CompositionContainer _Container;

    public MefControllerFactory(Assembly assembly)
    {
        _Container = new CompositionContainer(new AssemblyCatalog(assembly));
    }



    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType != null)
        {
            var controllers = _Container.GetExports<IController>();

            var controllerExport = controllers.Where(x => x.Value.GetType() == controllerType).FirstOrDefault();

            if (controllerExport == null)
            {
                return base.GetControllerInstance(requestContext, controllerType);
            }

            return controllerExport.Value;
        }
        else
        {
            throw new HttpException((Int32)HttpStatusCode.NotFound,
                String.Format(
                    "The controller for path '{0}' could not be found or it does not implement IController.",
                    requestContext.HttpContext.Request.Path
                )
            );
        }
    }
}

在 Global.asax.cs 我正在设置我的控制器工厂:

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

        RegisterRoutes(RouteTable.Routes);

        ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory.MefControllerFactory(Assembly.GetExecutingAssembly()));
    }

我有一个区域:

[Export(typeof(IController))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class HomeController : Controller
{
    private readonly IArticleService _articleService;

    [ImportingConstructor]
    public HomeController(IArticleService articleService)
    {
        _articleService = articleService;
    }

    //
    // GET: /Articles/Home/

    public ActionResult Index()
    {
        Article article = _articleService.GetById(55);

        return View(article);
    }

}

IArticleService 是一个接口。

有一个类实现IArticleService 并导出它。

它有效。

这就是我与 MEF 合作所需的一切吗?

如何跳过控制器的PartCreationPolicyImportingConstructor 设置?

我想使用构造函数设置我的依赖项。

PartCreationPolicy 丢失时,我得到以下异常:

控制器“MvcApplication4.Areas.Articles.Controllers.HomeController”的单个实例不能用于处理多个请求。如果正在使用自定义控制器工厂,请确保它为每个请求创建一个新的控制器实例。

【问题讨论】:

    标签: asp.net asp.net-mvc mef


    【解决方案1】:

    您的技术非常可靠,即使在部分信任的情况下也可以使用。 nerd dinner MEF 示例具有扩展,允许您按照约定处理发现控制器并将它们自动导入 MEF 导出,而无需使用 MEF 属性标记它们。但是直接管理零件目录在部分信任下不起作用,因此书呆子晚餐 MEF 技术在部分信任下不起作用。

    如果您在完全信任的情况下工作,并希望从 Nerd Dinner MEF 示例开始使用控制器进行基于约定的发现,但您可能还应该阅读有关 nerd Dinner MEF 示例的几个主要问题,如果您的自己的应用程序的模型在一个单独的类库项目中。我blogged about those cases 并提出了一些修复建议。

    如果您对基于约定的发现内容不那么感兴趣,那么书呆子晚餐样本就有点过度设计了。您的解决方案可能很好......并且也可以在部分信任下工作,这始终是一个奖励。

    [更新] 我确实发现了您的技术的一个潜在问题:

    var controllerExport = controllers.Where(x => x.Value.GetType() == 
    controllerType).FirstOrDefault();
    

    在此处的 where 子句中,您在部件集合中的每个导出上调用 .Value ......这实际上将导致每个导出都被组合和实例化以便进行评估。这可能是一个令人讨厌的性能问题。

    您可以考虑使用如下命名的导出合同来装饰您的控制器:

    [Export("Home", typeof(IController))]
    

    然后改用这样的控制器工厂:

    public class MefControllerFactory: IControllerFactory
    {
        private CompositionContainer _Container;
    
        public MefControllerFactory(Assembly assembly)
        {
            _Container = new CompositionContainer(new AssemblyCatalog(assembly));
        }
    
        #region IControllerFactory Members
    
        public IController CreateController(RequestContext requestContext, string controllerName)
        {
    
            var controller = _Container.GetExportedValue<IController>(controllerName);
    
            if (controller == null)
            {
                throw new HttpException(404, "Not found");
            }
    
            return controller;
        }
    
        public void ReleaseController(IController controller)
        {
           // nothing to do
        }
    
        #endregion
    }
    

    【讨论】:

      【解决方案2】:

      我决定回到 Unity。

      我创建了一个自定义属性而不是 MEF 的 ExportAttribute

      [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
      public class ImplementsAttribute : Attribute
      {
          public ImplementsAttribute(Type contractType)
          {
              ContractType = contractType;
          }
      
          public Type ContractType
          {
              get; 
              private set;
          }
      }
      

      例子:

      [Implements(typeof(ICustomerEmailService))]
      public class CustomerEmailService : ICustomerEmailService
      {...}
      

      还有一个自定义控制器工厂:

      public class MyControllerFactory : DefaultControllerFactory
      {
          private readonly IUnityContainer _container;
      
          public MyControllerFactory()
          {
              _container = new UnityContainer();
      
              Func<Type, bool> isController =
                  (type) => typeof(IController).IsAssignableFrom(type)
                          && (type.IsAbstract || type.IsInterface 
                              || type.GetCustomAttributes(typeof(GeneratedCodeAttribute), true).Any()) != true;
      
      
      
              foreach (Assembly assembly in BuildManager.GetReferencedAssemblies())
              {
                  try
                  {
                      var types = assembly.GetTypes();
      
                      // Also register all controllers
                      var controllerTypes = from t in types where isController(t) 
                                              select t;
      
      
                      foreach (Type t in controllerTypes)
                      {
                          _container.RegisterType(t);
                      }
      
      
                      // register all providers
                      var providers =
                          from t in types
                          from export in t.GetCustomAttributes(typeof(ImplementsAttribute), true).OfType<ImplementsAttribute>()
                          select new { export.ContractType, Provider = t };
      
                      foreach (var item in providers)
                      {
                          if (item.ContractType != null)
                          {
                              _container.RegisterType(item.ContractType, item.Provider);
                          }
                          else
                          {
                              _container.RegisterType(item.Provider);
                          }
                      }
                  }
                  catch 
                  {
                  }
              }
          }
      
      
      
          protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
          {
              if (controllerType != null)
              {
                  var controller = _container.Resolve(controllerType) as IController;
      
                  if (controller == null)
                  {
                      return base.GetControllerInstance(requestContext, controllerType);
                  }
      
                  return controller;
              }
      
      
              throw new HttpException((Int32)HttpStatusCode.NotFound,
                  String.Format(
                      "The controller for path '{0}' could not be found or it does not implement IController.",
                      requestContext.HttpContext.Request.Path)
              );
          }
      }
      

      解决MEF控制器工厂的所有问题对我来说太难了:(

      【讨论】:

        【解决方案3】:

        我最近一直在使用 MEF/MVC,并且一直在博客上介绍我修改后的 MEF+MVC 设计。我希望尽快将其推送到 CodePlex,但现在,看看这是否对您有任何帮助:

        1. Modular ASP.NET MVC using the Managed Extensibility Framework (MEF), Part One
        2. Modular ASP.NET MVC using the Managed Extensibility Framework (MEF), Part Two

        【讨论】:

        • 请记住,asp.net MVC 中的 MEF 在部分信任环境中不是很友好。例如,书呆子晚餐 MEF 样本在任何部分信任的情况下都会惊人地爆炸。基本上,在部分信任的情况下,您使用 MEF 的方式非常有限,因为部分信任的代码无法访问 MEF 的一些内部结构。
        • 我决定使用 Unity 而不是 MEF。
        • 提供的链接已失效。
        【解决方案4】:

        谢谢。

        我确实发现了您的技术的一个潜在问题:

        var controllerExport = controllers.Where(x => x.Value.GetType() == controllerType).FirstOrDefault();

        是的,这是真的。

        读完这篇文章 (http://codepaste.net/yadusn) 我明白了 NerdDinner 和 MEF 是如何完成的。

        我使用 MEF 的常规目录并创建了我的 MEFed 控制器工厂(控制器上没有 Export 属性)。

         public static IController GetController(CompositionContainer container, Type controllerType)
            {
                var controllers = container.GetExports<IController, IDictionary<string, object>>();
        
                if (controllers == null) return null;
        
                var controllerExport = controllers
                    .Where(exp => ExportMetadataContainsGuid(exp.Metadata, controllerType.GUID))
                    .FirstOrDefault();
        
                return (controllerExport == null) ? null : controllerExport.Value;
            }
        

        ExportMetadataContainsGuid 方法:

        public static bool ExportMetadataContainsGuid(IDictionary<string, object> metaData, Guid guid)
            {
                return metaData.ContainsKey(MetadataGuidKey) && guid == (Guid)metaData[MetadataGuidKey];
            }
        

        我使用元数据来存储类型的 GUID 并使用它来找到正确的控制器。

        【讨论】:

          猜你喜欢
          • 2012-11-14
          • 1970-01-01
          • 1970-01-01
          • 2014-02-24
          • 2013-11-13
          • 1970-01-01
          • 1970-01-01
          • 2014-02-04
          • 1970-01-01
          相关资源
          最近更新 更多