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!
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
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. –
Miałem ten problem, ale tylko podczas uruchamiania mojego kodu z innego rozwiązania. Okazuje się, że debugger ładował zespoły. – sdgfsdh