【问题标题】:CompositionContractMismatchException when trying to use MEF with MVC controller尝试将 MEF 与 MVC 控制器一起使用时的 CompositionContractMismatchException
【发布时间】:2013-01-14 10:46:05
【问题描述】:

我正在开发一个更大的 C# MVC 4 项目,该项目分为几个程序集(核心、域、后端 MVC、前端 MVC 等)。 我使用 MEF 提供的插件架构来加载和解决大多数依赖项。现在我还希望它用于加载 MVC 控制器。在几十个样本中发现的典型场景。

但我不断收到这个 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;
    }

一切都是这样配置的 DependencyResolver.SetResolver(new CustomDependencyResolver(container)); ControllerBuilder.Current.SetControllerFactory(new SomeCustomControllerFactory(container));

旁注:使用 MEF2 RegistrationBuilder 和具有三个 AssemblyCatalog 和一个 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 中的所有内容(空构造函数,没有导入等)。 MEF 容器装满了合适的出口。一切都很好。

经过一整天的调试和研究,我学到了很多关于 MEF 的知识,但仍然遇到同样的问题。希望有人可以在这里给我一个提示想错了。因为将所有内容移至新的 MVC 项目将非常耗时:-(

谢谢!

【问题讨论】:

    标签: c# asp.net-mvc-4 mef


    【解决方案1】:

    这看起来类似于System.InvalidCastException,当同一个程序集在不同上下文或不同位置加载两次时,有时会抛出该错误。 MEF 中的所有程序集加载都由AssemblyCatalog 类使用Assembly.Load(AssemblyName) 方法处理,该方法将在加载上下文中加载具有给定名称的程序集。但是,在certain conditions 下,它将在 LoadFrom 上下文中加载它,这有时会导致转换异常,例如您提到的那个:

    无法转换类型的基础导出值 "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 中与负载相关的条目。

    【讨论】:

    • 感谢您的回答。你是对的,它基本上是一个 System.InvalidCastException。又过了一天调试并阅读了您的链接后,我想通了。由于某些原因(我假设某些 .dll 深处有一些依赖项??)我不得不为整个 bin/* 目录添加另一个 DirectoryCatalog - 之后从 Lazy.Value 到控制器的转换工作完美。跨度>
    • 另一种可能发生这种情况的情况是一些测试运行者在包含您的测试的目录下创建的“TestResults”目录。 MEF 将递归到那些测试目录并找到相同 DLL 的副本。
    • 我遇到了这个问题,但仅在从另一个解决方案运行我的代码时。原来调试器正在加载程序集。
    【解决方案2】:

    我遇到了同样的问题,由多次加载相同的程序集引起。 将您的合同添加到单独的项目并引用输出 dll 而不是直接引用项目将解决此问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-18
      • 1970-01-01
      • 2013-07-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多