10

私は、いくつかのアセンブリ (コア、ドメイン、バックエンド MVC、フロントエンド MVC など) に分割された、より大きな C# MVC 4 プロジェクトに取り組んでいます。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(新しい SomeCustomControllerFactory(コンテナ));

補足: MEF2 RegistrationBuilder と、3 つの AssemblyCatalog と 1 つの DirectoryCatalog を持つ AggregateCatalog が使用されます。

メイン プロジェクト ソリューションから抽出し、新しい mvc 4 インターネット プロジェクト ソリューションを作成してそこに統合すると、すべてが完全に機能します。(2 つ目の単純なコア ライブラリを使用して、1 つのアセンブリでテストしました。)

私はすでにCompositionOptions.DisableSilentRejectionをオンにしました。そして、MEF関連のエラーをデバッグするためのこのリソースを見つけまし HomeController 内のすべてを削除しました (空のコンストラクター、インポートなしなど)。MEF コンテナーには、適切なエクスポートが格納されます。すべて順調です。

丸一日のデバッグと調査の後、私は MEF について多くのことを学びましたが、それでも同じ問題を抱えています。うまくいけば、誰かが私にヒントを与えてくれることを願っています。すべてを新しい MVC プロジェクトに移動すると、非常に時間がかかります :-(

ありがとう!

4

2 に答える 2

13

これは、同じアセンブリが異なるコンテキストまたは異なる場所から 2 回読み込まれたときにスローされることがあるSystem.InvalidCastExceptionに似ています。MEF でのすべてのアセンブリの読み込みは、 Assembly.Load(AssemblyName)メソッドを使用してAssemblyCatalogクラスによって処理されます。このメソッドは、Load コンテキストで指定された名前のアセンブリを読み込みます。ただし、特定の条件下では、 LoadFrom コンテキストにロードされ、これにより、言及したようなキャスト例外が発生することがあります。

タイプ "XY.HomeController(ContractName="XY.HomeController")" の基になるエクスポートされた値をタイプ "XY.HomeController" にキャストすることはできません。]

私がすることは、含まれているアセンブリがXY.HomeController複数の場所に展開されているかどうかを確認することです。厳密な名前のアセンブリである場合は、GAC を確認することを忘れないでください。

同様の問題がTelerik のフォーラムで言及されています。

このテーマの詳細については、「ランタイムがアセンブリを検索する方法」、「 アセンブリの読み込みのベスト プラクティス」 、およびSuzanne Cook の MSDN ブログの読み込み関連のエントリを参照してください。

于 2013-01-14T14:26:37.257 に答える