2012-09-13 10 views
6

私はこの質問を(特にタイトルの形で)表現するのが難しいと感じましたので、私にご負担ください。MEFを使用して相互依存モジュールを管理するにはどうすればよいですか?

私は、さまざまなことを行うために継続的に修正しているアプリケーションがあります。 MEFは機能のさまざまな部分を管理するのに適しているようです。大まかに言えば、一種のパ​​イプラインを形成するアプリケーションの三つのセクションがあります。それの最も単純な形式で

  1. 取得
  2. 変換

が、私は、これらの各段階を表現することができますインタフェースとして(IAcquisitionなど)。問題は、標準よりも豊富なデータを提供する取得コンポーネントを使用したいときに始まります。私は、この豊かなデータを使用するモジュールを設計したいと思いますが、そこに頼ることはできません。

もちろん、すべてのデータをインターフェイス仕様に追加することもできます。私は、例外をスローするかヌル値を返すことによって、より貧弱なデータソースを扱うことができます。これは理想から遠いようです。

私はMEFバインディングを3段階で行い、前に選択したものと互換性がある場合にのみ、モジュールがユーザーに提供されるようにしたいと思います。

私の質問利用可能なインポートセットを制限するメタデータを指定できますか?

例:

Acquision1がBasicDataを提供する唯一の

Acquision2が

Transformation1がBasicData

Transformation2が必要ですBasicDataとAdvancedData

Acquisi必要ですBasicDataとAdvancedData

を提供しています最初に選択モジュールが選択されます。

Acquisition1が選択されている場合は、変換2を提供しないでください。そうでない場合は、両方を提供してください。

これは可能ですか?もしそうなら、どうですか?今、あなたはあなたの買収、形質転換および発現のコンポーネントを持っている

public class BasicData 
{ 
    public string Basic { get; set; } // example data 
} 

public class AdvancedData : BasicData 
{ 
    public string Advanced { get; set; } // example data 
} 

答えて

4

あなたの質問は、このような構造を示唆しています。あなたは、異なる種類のデータを扱うことができるようにしたいので、彼らは汎用的だ:

public interface IAcquisition<out TDataKind> 
{ 
    TDataKind Acquire(); 
} 

public interface ITransformation<TDataKind> 
{ 
    TDataKind Transform(TDataKind data); 
} 

public interface IExpression<in TDataKind> 
{ 
    void Express(TDataKind data); 
} 

そして今、あなたはこのようになり、それらのうちのパイプラインを構築したい:

IExpression.Express(ITransformation.Transform(IAcquisition.Acquire)); 

それでは、パイプラインビルダーの構築を始めましょう:

using System; 
using System.Collections.Generic; 
using System.ComponentModel.Composition; 
using System.ComponentModel.Composition.Hosting; 
using System.ComponentModel.Composition.Primitives; 
using System.Linq; 
using System.Linq.Expressions; 

// namespace ... 

public static class PipelineBuidler 
{ 
    private static readonly string AcquisitionIdentity = 
     AttributedModelServices.GetTypeIdentity(typeof(IAcquisition<>)); 
    private static readonly string TransformationIdentity = 
     AttributedModelServices.GetTypeIdentity(typeof(ITransformation<>)); 
    private static readonly string ExpressionIdentity = 
     AttributedModelServices.GetTypeIdentity(typeof(IExpression<>)); 

    public static Action BuildPipeline(ComposablePartCatalog catalog, 
     Func<IEnumerable<string>, int> acquisitionSelector, 
     Func<IEnumerable<string>, int> transformationSelector, 
     Func<IEnumerable<string>, int> expressionSelector) 
    { 
     var container = new CompositionContainer(catalog); 

クラスは3つの契約のインタフェースのためのMEF型アイデンティティを保持しています。適切な輸出品を特定するために、後でそれらが必要になります。我々のBuildPipelineメソッドはActionを返します。それはパイプラインになるので、我々はちょうどpipeline()を行うことができます。 ComposablePartCatalogと3つのFunc(エクスポートを選択する)が必要です。そうすれば、私たちはこのクラスのすべての汚い作業を保つことができます。その後、まずCompositionContainerを作成します。

今、私たちが最初に取得コンポーネントのために、ImportDefinition Sを構築する必要があります。

 var aImportDef = new ImportDefinition(def => (def.ContractName == AcquisitionIdentity), null, ImportCardinality.ZeroOrMore, true, false); 

このImportDefinitionは単にIAcquisition<>インタフェースのすべての輸出を除外されます。今、私たちは、コンテナにそれを与えることができます。

 var aExports = container.GetExports(aImportDef).ToArray(); 

aExports今カタログ内のすべてのIAcquisition<>輸出を保持しています。それでは、この上でセレクタを実行してみましょう:

 var selectedAExport = aExports[acquisitionSelector(aExports.Select(export => export.Metadata["Name"] as string))]; 

そしてそこに我々は取得コンポーネントを持っている:

 var acquisition = selectedAExport.Value; 
     var acquisitionDataKind = (Type)selectedAExport.Metadata["DataKind"]; 

は、今、私たちは、形質転換および発現のコンポーネントに対して同じことをするつもりはなく、1としていますわずかな違い:ImportDefinitionは、各コンポーネントが前のコンポーネントの出力を処理できることを確実にします。

 var tImportDef = new ImportDefinition(def => (def.ContractName == TransformationIdentity) && ((Type)def.Metadata["DataKind"]).IsAssignableFrom(acquisitionDataKind), 
      null, ImportCardinality.ZeroOrMore, true, false); 
     var tExports = container.GetExports(tImportDef).ToArray(); 
     var selectedTExport = tExports[transformationSelector(tExports.Select(export => export.Metadata["Name"] as string))]; 

     var transformation = selectedTExport.Value; 
     var transformationDataKind = (Type)selectedTExport.Metadata["DataKind"]; 

     var eImportDef = new ImportDefinition(def => (def.ContractName == ExpressionIdentity) && ((Type)def.Metadata["DataKind"]).IsAssignableFrom(transformationDataKind), 
      null, ImportCardinality.ZeroOrMore, true, false); 
     var eExports = container.GetExports(eImportDef).ToArray(); 
     var selectedEExport = eExports[expressionSelector(eExports.Select(export => export.Metadata["Name"] as string))]; 

     var expression = selectedEExport.Value; 
     var expressionDataKind = (Type)selectedEExport.Metadata["DataKind"]; 

そして今、我々は、式ツリー内のすべてをつなぎ合わせることができます。

 var acquired = Expression.Call(Expression.Constant(acquisition), typeof(IAcquisition<>).MakeGenericType(acquisitionDataKind).GetMethod("Acquire")); 
     var transformed = Expression.Call(Expression.Constant(transformation), typeof(ITransformation<>).MakeGenericType(transformationDataKind).GetMethod("Transform"), acquired); 
     var expressed = Expression.Call(Expression.Constant(expression), typeof(IExpression<>).MakeGenericType(expressionDataKind).GetMethod("Express"), transformed); 
     return Expression.Lambda<Action>(expressed).Compile(); 
    } 
} 

をそして、それはそれです!簡単なサンプルアプリケーションは、次のようになります。

[Export(typeof(IAcquisition<>))] 
[ExportMetadata("DataKind", typeof(BasicData))] 
[ExportMetadata("Name", "Basic acquisition")] 
public class Acquisition1 : IAcquisition<BasicData> 
{ 
    public BasicData Acquire() 
    { 
     return new BasicData { Basic = "Acquisition1" }; 
    } 
} 

[Export(typeof(IAcquisition<>))] 
[ExportMetadata("DataKind", typeof(AdvancedData))] 
[ExportMetadata("Name", "Advanced acquisition")] 
public class Acquisition2 : IAcquisition<AdvancedData> 
{ 
    public AdvancedData Acquire() 
    { 
     return new AdvancedData { Advanced = "Acquisition2A", Basic = "Acquisition2B" }; 
    } 
} 

[Export(typeof(ITransformation<>))] 
[ExportMetadata("DataKind", typeof(BasicData))] 
[ExportMetadata("Name", "Basic transformation")] 
public class Transformation1 : ITransformation<BasicData> 
{ 
    public BasicData Transform(BasicData data) 
    { 
     data.Basic += " - Transformed1"; 
     return data; 
    } 
} 

[Export(typeof(ITransformation<>))] 
[ExportMetadata("DataKind", typeof(AdvancedData))] 
[ExportMetadata("Name", "Advanced transformation")] 
public class Transformation2 : ITransformation<AdvancedData> 
{ 
    public AdvancedData Transform(AdvancedData data) 
    { 
     data.Basic += " - Transformed2"; 
     data.Advanced += " - Transformed2"; 
     return data; 
    } 
} 

[Export(typeof(IExpression<>))] 
[ExportMetadata("DataKind", typeof(BasicData))] 
[ExportMetadata("Name", "Basic expression")] 
public class Expression1 : IExpression<BasicData> 
{ 
    public void Express(BasicData data) 
    { 
     Console.WriteLine("Expression1: {0}", data.Basic); 
    } 
} 

[Export(typeof(IExpression<>))] 
[ExportMetadata("DataKind", typeof(AdvancedData))] 
[ExportMetadata("Name", "Advanced expression")] 
public class Expression2 : IExpression<AdvancedData> 
{ 
    public void Express(AdvancedData data) 
    { 
     Console.WriteLine("Expression2: ({0}) - ({1})", data.Basic, data.Advanced); 
    } 
} 


class Program 
{ 
    static void Main(string[] args) 
    { 
     var pipeline = PipelineBuidler.BuildPipeline(new AssemblyCatalog(typeof(Program).Assembly), StringSelector, StringSelector, StringSelector); 
     pipeline(); 
    } 

    static int StringSelector(IEnumerable<string> strings) 
    { 
     int i = 0; 
     foreach (var item in strings) 
      Console.WriteLine("[{0}] {1}", i++, item); 
     return int.Parse(Console.ReadLine()); 
    } 
} 
+0

このような完全な回答をいただきありがとうございます。 –

関連する問題