2010-06-13 9 views
7

MEF를 asp.net mvc와 함께 사용하고 싶습니다. 나는 내 컨트롤러 공장을 설정하고있어MEF 및 ASP.NET MVC

Global.asax.cs에서
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 
       ) 
      ); 
     } 
    } 
} 

: 나는 다음과 같은 컨트롤러 공장을 썼다

[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가 있습니다 :

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

     RegisterRoutes(RouteTable.Routes); 

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

나는 지역이 인터페이스.

IArticleService을 구현하고 내보내는 클래스가 있습니다.

작동합니다.

MEF에서 필요한 모든 것입니까?

컨트롤러에 대해 PartCreationPolicyImportingConstructor 설정을 건너 뛸 수 있습니까?

생성자를 사용하여 내 의존성을 설정하고 싶습니다. PartCreationPolicy이없는 때, 나는 예외 다음 얻을

은 :

컨트롤러의 단일 인스턴스 'MvcApplication4.Areas.Articles.Controllers.HomeController는'여러 요청을 처리하는 데 사용할 수 없습니다. 사용자 정의 컨트롤러 팩토리가 사용 중이면 각 요청에 대해 컨트롤러의 새 인스턴스를 생성해야합니다.

답변

2

나는 단결로 돌아 가기로 결정했습니다.

나는 사용자 지정 특성을 만드는 대신 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 디자인에 대해 블로깅을하고 있습니다. 나는 곧 코드 플렉스에 밀어 바라고 있지만,이 중 하나가 당신을 도움이된다면 지금은 볼 수 있어요 :

  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 사용 방법이 매우 제한적입니다. –

+0

MEF 대신 Unity를 사용하기로 결정했습니다. –

4

여기에 귀하의 기술은 매우 견고하고 의지 부분 신뢰에서도 일한다. nerd dinner MEF 예제에는 관례에 따라 컨트롤러를 발견하고 MEF 속성으로 플래그 지정하지 않고 MEF 내보내기를 자동으로 수행 할 수있는 확장이 있습니다. 그러나 부분 카탈로그를 관리하는 것은 부분 신뢰에서 직접 작동하지 않으므로 괴상한 저녁 MEF 기법은 부분 신뢰로 작동하지 않습니다.

당신이 완전히 신뢰하는 마음으로 일하면서 컨트롤러와 함께 관습 기반 검색을 Nerd Dinner MEF 예제로 시작하고 싶다면 다음과 같은 몇 가지 주요 문제점을 읽어야합니다. 자신의 응용 프로그램의 모델은 라이브러리 프로젝트와는 별도의 클래스에 있습니다. I blogged about those cases 및 몇 가지 수정 사항을 제안했습니다.

만약 당신이 대회에 기반한 발견 재료에 관심이 없다면, 괴상한 저녁 식사 샘플은 약간 과소 설계되어 있습니다. 귀하의 솔루션은 아마 괜찮을 것입니다 ... 그리고 부분 보너스에서도 작동합니다. 항상 보너스입니다.

[업데이트] 난 당신의 기술을 하나의 잠재적 인 문제를 발견 한 다음에

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

경우 여기 절, 당신은 부품의 컬렉션의 각 수출에 .Value를 호출 ... 있음 실제로 이러한 수출품 각각을 평가하고 구성하기 위해 구성 및 인스턴스화 할 것입니다. 그것은 불쾌한 성능 문제 일 수 있습니다.

는이 같은라는 이름의 수출 계약으로 컨트롤러를 장식 고려해 볼 수 있습니다 : 그런 다음이 대신 같은 controler를 팩토리를 사용

[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

감사합니다.

난 당신의 기술을 하나의 잠재적 인 문제를 발견했다

:

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

예. 그렇습니다.

이 글을 읽은 후 (http://codepaste.net/yadusn) MEF와 함께 NerdDinner가 어떻게 수행되었는지 알았습니다.

MEF에 대한 기존 카탈로그를 사용하고 컨트롤러에 내보내기 특성이없는 MEFed 컨트롤러 팩터 리를 만들었습니다.

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에 대한 메타 데이터를 사용하여 오른쪽 컨트롤러를 찾기 위해 그것을 사용할 수 있습니다.