2010-06-13 99 views
7

我想使用MEF和asp.net mvc。 我写了下面的控制器工厂: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”不能被用来处理多个请求。如果自定义控制器工厂正在使用中,请确保它为每个请求创建控制器的新实例。

回答

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控制器工厂的所有问题:(

1

最近我一直在使用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
+0

请记住,asp.net MVC中的MEF在部分信任环境中不太友好。例如,在任何部分信任的情况下,书呆子晚餐MEF样品将会非常壮观。基本上,在部分信任方面,由于部分信任的代码无法访问几个MEF内部,所以在使用MEF的方式方面非常有限。 – 2010-07-08 05:25:18

+0

我决定使用Unity代替MEF。 – 2010-08-15 23:47:12

4

这里你的技术是非常稳固,而且会即使部分信任也能工作。 nerd dinner MEF示例具有扩展功能,允许您按照约定处理发现控制器,并自动将其转换为MEF导出,而不用MEF属性标记它们。但直接管理零件目录不能用于部分信任,所以书呆子晚餐MEF技术不能用于部分信任。

如果你是在完全信任的环境下工作,并希望以你的控制器为基础的约定发现以Nerd晚宴MEF为例开始,但你也应该阅读关于几个主要问题的书呆子晚餐MEF例子,你自己的应用程序的模型是在一个单独的类库项目。我blogged about those cases并提出了一些修复。

如果你不是所有对基于约定的发现东西感兴趣,那么书呆子晚餐样品是一个过度工程设计。你的解决方案可能很好,并且也可以在部分信任中工作,这总是一种奖励。

[更新] 我没有发现一个潜在的问题与你的技术:

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

在哪里这里子句,你是在零件的集合中的每个出口调用.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 
} 
0

感谢。

我没有发现一个潜在的问题与您的技术:

变种controllerExport = controllers.Where(X => x.Value.GetType()== controllerType).FirstOrDefault();

是的,的确如此。

看完这个后(http://codepaste.net/yadusn)我明白NerdDinner和MEF是如何完成的。

我使用MEF的传统目录并创建了我的MEFed控制器工厂(控制器上没有导出属性)。

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

我使用元数据存储类型的GUID,并用它来寻找合适的控制器:

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方法。