6

序章

クラスSessionModelは、いくつかのサービスを提供するサービス ロケーターです (システム アーキテクチャについては後で詳しく説明しますが、今のところはそのようにする必要があります)。

コード

次のコード部分を短く、自己完結型で、正しい (コンパイル可能)、例 (SSCCE) になるように編集しました。

using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

namespace ConsoleApplication1
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var sessionModel = new SessionModel(3);

            // first case (see text down below):
            var compositionContainer = new CompositionContainer();

            // second case (see text down below):
            //var typeCatalog = new TypeCatalog(typeof (SessionModel));
            //var compositionContainer = new CompositionContainer(typeCatalog);

            compositionContainer.ComposeExportedValue(sessionModel);

            var someService = compositionContainer.GetExportedValue<ISomeService>();
            someService.DoSomething();
        }
    }

    public class SessionModel
    {
        private int AValue { get; set; }

        [Export]
        public ISomeService SomeService { get; private set; }

        public SessionModel(int aValue)
        {
            AValue = aValue;
            // of course, there is much more to do here in reality:
            SomeService = new SomeService();
        }
    }

    public interface ISomeService
    {
        void DoSomething();
    }

    public class SomeService : ISomeService
    {
        public void DoSomething()
        {
            Console.WriteLine("DoSomething called");
        }
    }
}

問題

他のパーツを構成するときに、サービス ロケーターによってエクスポートされたパーツ (つまり ) を MEF に考慮してもらいたいのですSomeServiceが、残念ながらこれは機能しません。

最初のケース

エクスポートされた値を取得しようとすると、このコントラクト名と必要な型 ID ( ) を持つエクスポートが存在しないというメッセージがISomeService表示されます。System.ComponentModel.Composition.ImportCardinalityMismatchExceptionConsoleApplication1.ISomeService

2 番目のケース

CompositionContainer例外を使用して作成するとTypeCatalog、例外が少し異なります。これは、System.ComponentModel.Composition.CompositionExceptionMEF が を作成する方法を見つけられないことを示していますConsoleApplication1.SessionModel(これは正しいことであり、私が自分で作成している理由です)。

追加情報

mefx両方の場合について次のように述べています。

[Part] ConsoleApplication1.SessionModel from: DirectoryCatalog (Path=".")
  [Export] ConsoleApplication1.SessionModel.SomeService (ContractName="ConsoleApplication1.ISomeService")

[Part] ConsoleApplication1.SessionModel from: AssemblyCatalog (Assembly="ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
  [Export] ConsoleApplication1.SessionModel.SomeService (ContractName="ConsoleApplication1.ISomeService")

どうすればいいですか?これは MEF で可能ですか、それとも Unity や StructureMap などを使用する必要がありますか? これは実装できますExportProviderか?

4

4 に答える 4

1

OK、それが私がやった方法です:

私は独自のSessionModelExportProvider調査結果のエクスポートを実装しましたSessionModel(以下のコードを参照)。クラスSessionModelExportは、エクスポート データを保持するためのものであり、サービスのインスタンスを作成する代わりに、のプロパティの値を返しますSessionModel

public class SessionModelExportProvider : ExportProvider
{
    private List<Export> Exports { get; set; }

    public SessionModelExportProvider(SessionModel sessionModel)
    {
        // get all the properties of the session model having an Export attribute
        var typeOfSessionModel = typeof (SessionModel);
        PropertyInfo[] properties = typeOfSessionModel.GetProperties();
        var propertiesHavingAnExportAttribute =
            from p in properties
            let exportAttributes = p.GetCustomAttributes(typeof (ExportAttribute), false)
            where exportAttributes.Length > 0
            select new
                       {
                           PropertyInfo = p,
                           ExportAttributes = exportAttributes
                       };

        // creating Export objects for each export
        var exports = new List<Export>();
        foreach (var propertyHavingAnExportAttribute in propertiesHavingAnExportAttribute)
        {
            var propertyInfo = propertyHavingAnExportAttribute.PropertyInfo;
            foreach (ExportAttribute exportAttribute in propertyHavingAnExportAttribute.ExportAttributes)
            {
                string contractName = exportAttribute.ContractName;
                if (string.IsNullOrEmpty(contractName))
                {
                    Type contractType = exportAttribute.ContractType ?? propertyInfo.PropertyType;
                    contractName = contractType.FullName;
                }

                var metadata = new Dictionary<string, object>
                                   {
                                       {CompositionConstants.ExportTypeIdentityMetadataName, contractName},
                                       {CompositionConstants.PartCreationPolicyMetadataName, CreationPolicy.Shared}
                                   };
                var exportDefinition = new ExportDefinition(contractName, metadata);
                var export = new SessionModelExport(sessionModel, propertyInfo, exportDefinition);
                exports.Add(export);
            }
        }

        Exports = exports;
    }

    protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition,
                                                          AtomicComposition atomicComposition)
    {
        return Exports.Where(e => definition.IsConstraintSatisfiedBy(e.Definition));
    }
}

public class SessionModelExport : Export
{
    private readonly SessionModel sessionModel;
    private readonly PropertyInfo propertyInfo;
    private readonly ExportDefinition definition;

    public SessionModelExport(SessionModel sessionModel, PropertyInfo propertyInfo, ExportDefinition definition)
    {
        this.sessionModel = sessionModel;
        this.propertyInfo = propertyInfo;
        this.definition = definition;
    }

    public override ExportDefinition Definition
    {
        get { return definition; }
    }

    protected override object GetExportedValueCore()
    {
        var value = propertyInfo.GetValue(sessionModel, null);
        return value;
    }
}
于 2013-09-04T10:08:26.090 に答える
0

問題は、 SomeService がインスタンス プロパティであることです。システムに複数の SessionModel オブジェクトが存在する可能性がありますが、MEF は、どの SessionModel がインポートに一致するはずの ISomeService インスタンスを返しているかを知る方法がありません。

代わりに、SessionModel を静的クラスにし、SomeService を静的プロパティにします。または、SessionModel をシングルトンにします。SomeService プロパティは引き続き静的ですが、SessionModel の唯一のインスタンスからサービスをエクスポートします。

于 2013-08-06T21:20:50.760 に答える
0

最初のケース: あなたに渡すことで、 の部分ではなくtype の部分を追加sessionModelします。このケースを機能させるには、サービスを ComposeExportedValue に渡す必要があります。ComposeExportedValueSessionModelISomeService

compositionContainer.ComposeExportedValue(sessionModel.SomeService);

2 番目のケース: このケースでは、パーツの作成をコンテナーに任せます。パラメーターのないコンストラクター、またはImportingConstructorAttributeで装飾されたパラメーターを持つコンストラクターがある場合、コンテナーは新しいパーツを作成できます。これはおそらく、デザインを少し変更する必要があることを意味します。

個人的には最初のケースを使用しますが、これを最小限に抑えるようにしてください。結局、MEF の通常の (そして推奨される) 使用法は、コンテナーにパーツを作成させて処理させることです。

于 2013-08-21T07:25:04.537 に答える