2013-01-14 2 views
9

저는 여러 어셈블리 (코어, 도메인, 백엔드 MVC, 프론트 엔드 MVC 등)로 분할 된 더 큰 C# MVC 4 프로젝트에서 작업하고 있습니다. MEF에서 제공하는 플러그인 아키텍처를 사용하여 대부분의 종속성을로드하고 해결합니다. 이제 MVC 컨트롤러를로드하는 데 사용하기를 원했습니다. 일반적인 시나리오는 수십 개의 샘플에서 발견됩니다.MVC 컨트롤러에서 MEF를 사용하려고하면 CompositionContractMismatchException이 발생합니다.

예외는 말한다 :

는하지만이 YSOD가 계속

[CompositionContractMismatchException: Cannot cast the underlying exported value of type "XY.HomeController (ContractName="XY.HomeController")" to type "XY.HomeController".] 
System.ComponentModel.Composition.ExportServices.CastExportedValue(ICompositionElement element, Object exportedValue) +505573 
System.ComponentModel.Composition.<>c__DisplayClass10`2.<CreateSemiStronglyTypedLazy>b__c() +62 
System.Lazy`1.CreateValue() +14439352 
System.Lazy`1.LazyInitValue() +91 
    XY.DependencyManagement.SomeCustomControllerFactory.GetControllerInstance(RequestContext requestContext, Type controllerType) in (Path)\Core\DependencyManagement\SomeCustomControllerFactory.cs:32 
System.Web.Mvc.DefaultControllerFactory.CreateController(RequestContext requestContext, String controllerName) +89 
System.Web.Mvc.MvcHandler.ProcessRequestInit(HttpContextBase httpContext, IController& controller, IControllerFactory& factory) +305 
System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +87 
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +12550291 
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +288 

사용자 정의 ControllerFactory :

public class SomeCustomControllerFactory : DefaultControllerFactory { 

    private readonly CompositionContainer _compositionContainer; 

    public SomeCustomControllerFactory (CompositionContainer compositionContainer) { 
     _compositionContainer = compositionContainer; 
    } 

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

     IController result; 

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

     return result; 
    } 

protected override Type GetControllerType(RequestContext requestContext, string controllerName) { 

     Type controllerType = base.GetControllerType(requestContext, controllerName); 

// used to find objects in the container which assemblies are in a sub directory and not discovered by MVC 
// TODO: only create parts that are used 
     if (controllerType == null && this._compositionContainer != null && 
      this._compositionContainer != null) { 

      var controllerTypes = 
       this._compositionContainer.GetExports<Controller, IDictionary<string, object>>() 
        .Where(
         e => 
         e.Value.GetType().Name.ToLowerInvariant() == 
         controllerName.ToLowerInvariant() + ControllerNameByConvention) 
        .Select(e => e.Value.GetType()).ToList(); 

      switch (controllerTypes.Count) { 
       case 0: 
        controllerType = null; 
        break; 
       case 1: 
        controllerType = controllerTypes.First(); 
        break; 
       case 2: 
        throw CreateAmbiguousControllerException(requestContext.RouteData.Route, controllerName, 
                  controllerTypes); 
      } 
     } 

     return controllerType; 
    } 

그리고 CustomDependencyResolver :

public class CustomDependencyResolver : IDependencyResolver { 

    private readonly CompositionContainer _container; 
    public CustomDependencyResolver(CompositionContainer container) { 
     _container = container; 
} 
    public IDependencyScope BeginScope() { 
     return (IDependencyScope)this; 
    } 

    public object GetService(Type serviceType) { 
     var export = _container.GetExports(serviceType, null, null).SingleOrDefault(); 

     return null != export ? export.Value : null; 
    } 

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

     if (exports.Any()) { 
      foreach (var export in exports) { 
       createdObjects.Add(export.Value); 
      } 
     } 

     return createdObjects; 
    } 

복음 전도, erything이 이렇게 구성되었습니다. DependencyResolver.SetResolver (새 CustomDependencyResolver (container)); ControllerBuilder.Current.SetControllerFactory (새 SomeCustomControllerFactory (container));

사이드 노트 : MEF2 RegistrationBuilder와 3 개의 AssemblyCatalog 및 1 개의 DirectoryCatalog가있는 AggregateCatalog가 사용됩니다.

주 프로젝트 솔루션에서 추출한 다음 새로운 MVC 4 인터넷 프로젝트 솔루션을 만들고 거기에 통합하면 모든 것이 완벽하게 작동합니다. (하나의 어셈블리로 두 번째 간단한 코어 라이브러리로 테스트했습니다.)

이미 CompositionOptions.DisableSilentRejection을 켰습니다. 그리고 MEF 관련 오류를 디버그하기 위해이 리소스를 찾았습니다. https://blogs.msdn.com/b/dsplaisted/archive/2010/07/13/how-to-debug-and-diagnose-mef-failures.aspx?Redirected=true HomeController (Empty 생성자, 가져 오기 없음 등)의 모든 것을 제거했습니다. MEF 컨테이너는 적절한 수출로 가득 차 있습니다. 다 괜찮아.

디버깅과 연구 하루 종일 MEF에 대해 많은 것을 배웠지 만 여전히 동일한 문제가 있습니다. 바라건대 누군가 나에게 힌트를 줄 수 있기를 바랍니다. 새로운 MVC 프로젝트에 이르기까지 모든 이동 원인이 될 것이다 아주 아주 많은 시간이 소요

감사합니다!

:-(

답변

12

이 동일한 어셈블리가 다른 contexes 또는에서 두 번로드 될 때 종종 발생하는 System.InvalidCastException과 유사 MEF의 모든 어셈블리로드는 Assembly.Load(AssemblyName) 메서드를 사용하여 AssemblyCatalog 클래스에서 처리됩니다.이 메서드는 Load 컨텍스트에서 지정된 이름으로 어셈블리를로드하지만, certain conditions 아래에서로드 컨텍스트로로드합니다. 언급 한 것과 같은 전송 예외를 발생시킵니다.

,

유형 의 기본 보낸 값을 캐스팅 할 수 없습니다 "XY.HomeController (ContractName ="XY.HomeController ")는" "XY.HomeController"를 입력합니다.]

는 내가 뭘 할 것은 어셈블리 경우에 볼 것이다 두 개 이상의 위치에 XY.HomeController이 배포되어 있습니다. 강력한 지명 된 어셈블리 인 경우 GAC를 살펴 보는 것을 잊지 마십시오.

유사한 문제는 Telerik's forum에 언급되어 있습니다.

이 주제에 대한 자세한 내용은 "How the Runtime Locates Assemblies""Best Practices for Assembly Loading" 및로드 관련 항목 Suzanne Cooks MSDN blog을 참조하십시오.

+0

답장을 보내 주셔서 감사합니다. 당신은 기본적으로 System.InvalidCastException입니다. 디버깅과 링크를 읽은 다른 날 후에 나는 그것을 알아 낸 것입니다. 몇 가지 이유로 (일부 종속성을 일부 .dll의 깊숙한 부분에 깊이 있다고 가정합니다.) 전체 bin/* 디렉토리에 다른 DirectoryCatalog를 추가해야했습니다. * 그 이후 게으른 <>에서 컨트롤러로 값을 캐스팅하면 완벽하게 작동합니다. – Andreas

+2

또 다른 경우는 일부 테스트 주자가 테스트가 포함 된 디렉토리 아래에 만드는 "TestResults"디렉토리입니다. MEF는 테스트 디렉토리로 돌아가서 동일한 DLL의 복사본을 찾습니다. –

+1

다른 솔루션에서 코드를 실행하는 경우에만이 문제가 발생했습니다. 디버거가 어셈블리를로드하고 있음을 나타냅니다. – sdgfsdh

관련 문제