2013-01-14 9 views
9

Pracuję nad większym projektem C# MVC 4, który jest podzielony na kilka zespołów (Core, Domain, Backend MVC, Frontend MVC itd.). Używam architektury wtyczki dostarczonej przez MEF do ładowania i rozwiązywania większości zależności. Teraz chciałem go również użyć do załadowania kontrolera MVC. Typowy scenariusz znaleziony w dziesiątkach próbek.CompositionContractMismatchException podczas próby użycia MEF z kontrolerem MVC

ale wciąż otrzymuję ten YSOD:

Wyjątkiem mówi:

[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 

Zwyczaj 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; 
    } 

I 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; 
    } 

Ev erything jest skonfigurowany w ten sposób DependencyResolver.SetResolver (new CustomDependencyResolver (container)); ControllerBuilder.Current.SetControllerFactory (new SomeCustomControllerFactory (container));

Nota boczna: Używane są MEF2 RegistrationBuilder i AggregateCatalog z trzema AssemblyCatalogs i jedną DirectoryCatalog.

Wszystko działa idealnie, jeśli wydobędę je z głównego rozwiązania projektowego i utworzę nowe rozwiązanie projektu internetowego mvc 4 i tam je zintegruję. (Przetestowano go z jednym zestawem, z drugą prostą biblioteką lib.)

Już włączyłem CompositionOptions.DisableSilentRejection. I znalazłem ten zasób do debugowania błędów związanych z MEF https://blogs.msdn.com/b/dsplaisted/archive/2010/07/13/how-to-debug-and-diagnose-mef-failures.aspx?Redirected=true Usunąłem wszystko w HomeController (Pusty konstruktor, bez importu itp.). Pojemnik MEF jest wypełniony odpowiednim wywozem. Wszystko w porządku.

Po całym dniu debugowania i badań nauczyłem się dużo o MEF, ale nadal mam ten sam problem. Mam nadzieję, że ktoś może dać mi wskazówkę, że chce czegoś złego tutaj. Przyczyna przeniesienie wszystkiego do nowego projektu MVC byłoby bardzo czasochłonne :-(

Dzięki!

Odpowiedz

12

To wygląda podobnie do System.InvalidCastException że czasami jest generowany, gdy ten sam zespół jest załadowany dwa razy w różnych contexes lub różne lokalizacje.Każdy ładunek złożenia w MEF jest obsługiwany przez klasę AssemblyCatalog przy użyciu metody Assembly.Load(AssemblyName), która załaduje zespół o podanej nazwie w kontekście Załaduj, jednak pod certain conditions załaduje go w kontekście LoadFrom, co może czasem powodują wyjątki odlewania, takie jak wspomniane:

Nie można rzutować stanowiącego podstawę eksportowany wartość typu "XY.HomeController (ContractName =" XY.HomeController ")" wpisać "XY.HomeController".]

Co chciałbym zrobić, to patrzeć jeśli zespole która zawiera XY.HomeController jest wdrażana w więcej niż jednej lokalizacji. Nie zapomnij zajrzeć do GAC, jeśli jest to mocny nazwany zespół.

Podobny problem został wymieniony w Telerik's forum.

Aby uzyskać więcej informacji na ten temat, należy zapoznać się z "How the Runtime Locates Assemblies""Best Practices for Assembly Loading", a także z wpisami związanymi z obciążeniem w Suzanne Cooks MSDN blog.

+0

Dzięki za odpowiedź. Masz rację, to w zasadzie wyjątek System.InvalidCastException. Po kolejnym debugowaniu i przeczytaniu twoich linków odkryłem to. Z jakichś powodów (zakładam pewne zależności głęboko w niektórych plikach .dll?) Musiałem dodać kolejny DirectoryCatalog dla całego katalogu bin/* - po tym obsadzie z Lazy <>. Wartość do kontrolera działa idealnie. – Andreas

+2

Innym przypadkiem, w którym może się to zdarzyć, jest katalog "TestResults", który niektórzy testerzy tworzą w katalogu zawierającym twoje testy. MEF powróci do tych katalogów testowych i znajdzie kopie tych samych bibliotek DLL. –

+1

Miałem ten problem, ale tylko podczas uruchamiania mojego kodu z innego rozwiązania. Okazuje się, że debugger ładował zespoły. – sdgfsdh

Powiązane problemy