2

WPF MEF アプリケーションを作成しています。過去に、私は WPF IoC ベースのアプリケーションを作成し、PRISM を使用してコードのモジュールを構造化しました。

  • シェル- メイン実行可能ファイル
  • BusinessArea.Module.Interface - BusinessArea モジュールのサービス、ViewModel、およびビューのすべてのインターフェイスが含まれています。
  • BusinessArea.Module - PRISM IModule の実装を含み、BusinessArea.Module.Interface プロジェクトにインターフェイスを実装します。
  • OtherBusinessArea.Module.Interface - 別のモジュール
  • OtherBusinessArea.Module - 別のモジュール インターフェイス

IoC の世界では、各モジュールはロード時にコンポーネントを IoC コンテナーに登録します。2 つのモジュールでコンポーネントを相互に再利用したい場合は、インターフェイス プロジェクトの 1 つを参照し、コンポーネントを挿入するだけで済みます。

ただし、MEF では、パーツをモジュールに分割したり、この種のモジュール間でコンポーネントを再利用したりするための優れたプラクティスやガイドラインを実際に見つけることができません。5 つの質問があります。

  1. すべてのパーツのインターフェースを作成し続ける必要がありますか?
  2. アプリケーション全体またはモジュールごとに 1 つの共有コンテナーを使用する必要があります (各モジュールは、Windows 8 スタイルのスタート メニューから起動される個別のアプリのようなものです)。
  3. あるモジュールが別のモジュールのパーツを使用したい場合、どうすれば分離を維持できますか?
  4. あるモジュールが別のコンテナのパーツを使用したい場合、どうすれば分離を維持できますか。
  5. 多数のモジュールを含むアプリケーションでパフォーマンスを高速に維持する最善の方法。
4

1 に答える 1

8
  1. はい、そうすべきです。モジュールをインターフェイスではなく独自の実装タイプとしてエクスポートする場合、モジュールをインポートする消費モジュールは、モジュールの実装を含むライブラリを参照する必要があります。IoC を使用する主な理由の 1 つは、これを回避することです。

  2. はい、1 つのコンテナが必要です。モジュールがコンテナーを保持している場合、コンテナーが存在する前にモジュールのインスタンスが必要なため、このモジュールをエクスポート/インポートすることはできません。ここには実際の MEF 固有の問題はありません。Unity などと同じです。PRISM アプリの場合、モジュールのインスタンス化と配線を 1 つの場所 (コンテナー) で分離するという考え方です。コンテナーは Bootstrapper で何よりも先に作成され、シェル、モジュール、サービスなど、必要なものがすべて作成されます。オブジェクトのインスタンス化と参照をまったく異なるコンテキストで管理するアプリケーション内に他の IoC コンテナーを用意することは理にかなっています。たとえば、UI 用ではなく、複雑なビジネス オブジェクトを結び付けるためです。また、それはメイン コンテナーが認識しない内部 (プライベート) コンテナーで MEF を使用してモジュール自体を構築することは理にかなっていますそれ自体が複合UIであるモジュールを備えた複合 UIがあるよりも。本当に必要な場合はよく考えてください。たとえば、アセンブリを2回ロードするなど、この種の問題に簡単に遭遇します。

  3. 前と同じように。モジュール B は、ModuleA のインターフェイス プロジェクトを参照し、タイプ IModuleA のフィールドまたはパラメーターをインポートします。コンテナーは依存関係を解決して ModuleA を注入します。

  4. 前に述べたように、アーキテクチャをまっすぐにする必要があります。モジュール間に依存関係を注入する場合は、それらが同じコンテナー内にある必要があります。それがIoCの考え方です。

  5. 複数の IoC コンテナーを使用する複雑なアプリケーションに取り組んでいます。UI には MEF を使用しています。これは、シェルといくつかの UI モジュールです。ビジネス ロジックに関連するものについては、AutoFac IoC コンテナーを使用します。主に、Autofac は「実際の」IoC コンテナーであり、MEF はそうではないためですが、はるかに高速であるためでもあります。Autofac は、MEF でできることは何でも実行できます。次回は、UI にも MEF の代わりに Autofac を使用します。

質問にはたくさんの質問があります....

そして、これは私が少し前に出した同様の質問に対する答えです。うまくいけばあなたにも役立ちます:

ここでは原則としてシステムを説明することしかできませんが、正しい方向に向けられるかもしれません。何に対しても常に数多くのアプローチがありますが、これが私がベスト プラクティスであると理解したものであり、私が非常に良い経験をしたものです。

ブートストラップ

MefBootstrapperPrism や Unity と同様に、すべてはinから派生した Bootstrapper から始まりMicrosoft.Practices.Prism.MefExtensionsます。ブートストラッパーは MEF コンテナーをセットアップし、サービス、ビュー、ViewModel、およびモデルを含むすべてのタイプをインポートします。

ビュー (モジュール) のエクスポート

これは、MatthiasG が参照している部分です。私の実践は、GUI モジュールの次の構造です。

  • モデルは、属性を使用して、それ自体を具象型としてエクスポートします (インターフェイスにすることもできます。MatthiasG を参照してください) [Export(typeof(MyModel)][PartCreationPolicy(CreationPolicy.Shared)]1 つのインスタンスのみが作成される (シングルトン動作) ことを示すために、 でマークします。

  • ViewModel は、モデルと同様に具象型として自身をエクスポートし、コンストラクター インジェクションを介してモデルをインポートします。

    [ImportingConstructor] public class MyViewModel(MyModel モデル) { _model = モデル; }

  • View は、ViewModel がモデルをインポートするのと同じ方法で、コンストラクター インジェクションを介して ViewModel をインポートします。

  • そして今、これは重要です。ビューは、「標準」[Export]属性から派生した特定の属性でそれ自体をエクスポートします。次に例を示します。

    [ViewExport(RegionName = RegionNames.DataStorageRegion)] public partial class DataStorageView { [ImportingConstructor] public DataStorageView(DataStorageViewModel viewModel) { InitializeComponent(); DataContext = ビューモデル; } }

[ViewExport] 属性

属性は次の 2 つのことを行います。属性[ViewExport]から派生するため[Export]、ビューをインポートするよう MEF コンテナーに指示します。何として?これはその定義に隠されています。コンストラクターのシグネチャは次のようになります。

public ViewExportAttribute() : base(typeof(UserControl)) {}

[Export]のコンストラクターを の型で呼び出すとUserControl、すべてのビューがUserControlMEF コンテナーとして登録されます。

RegionName次に、シェル UI のどのリージョンにビューをプラグインするかを後で決定するために使用されるプロパティを定義します。RegionName プロパティは、インターフェイスの唯一のメンバーですIViewRegionRegistration。属性クラス:

/// <summary>
/// Marks a UserControl for exporting it to a region with a specified name
/// </summary>
[Export(typeof(IViewRegionRegistration))]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
[MetadataAttribute]
public sealed class ViewExportAttribute : ExportAttribute, IViewRegionRegistration
{
    public ViewExportAttribute() : base(typeof(UserControl)) {}

    /// <summary>
    /// Name of the region to export the View to
    /// </summary>
    public string RegionName { get; set; }
}

ビューのインポート

さて、システムの最後の重要な部分は、シェルの領域にアタッチするビヘイビアAutoPopulateExportedViewsです: これにより、次の行で MEF コンテナーからすべてのモジュールがインポートされます。

[ImportMany] 
private Lazy<UserControl, IViewRegionRegistration>[] _registeredViews;

これにより、コンテナから として登録されているすべてのタイプがインポートされUserControlます (それらに を実装するメタデータ属性がある場合) IViewRegionRegistration。属性がそうであるため[ViewExport]、これは、 でマークされたすべてのタイプをインポートすることを意味します[ViewExport(...)]

最後のステップは、ビューをリージョンに接続することです。これは、bahvior がそのOnAttach()プロパティで行います。

/// <summary>
/// A behavior to add Views to specified regions, if the View has been exported (MEF) and provides metadata
/// of the type IViewRegionRegistration.
/// </summary>
[Export(typeof(AutoPopulateExportedViewsBehavior))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class AutoPopulateExportedViewsBehavior : RegionBehavior, IPartImportsSatisfiedNotification
{
    protected override void OnAttach()
    {
        AddRegisteredViews();
    }

    public void OnImportsSatisfied()
    {
        AddRegisteredViews();
    }

    /// <summary>
    /// Add View to region if requirements are met
    /// </summary>
    private void AddRegisteredViews()
    {
        if (Region == null) return;

        foreach (var view in _registeredViews
            .Where(v => v.Metadata.RegionName == Region.Name)
            .Select(v => v.Value)
            .Where(v => !Region.Views.Contains(v)))
            Region.Add(view);

    }

    [ImportMany()] 
    private Lazy<UserControl, IViewRegionRegistration>[] _registeredViews;
}

注意してください.Where(v => v.Metadata.RegionName == Region.Name)。これは、属性の RegionName プロパティを使用して、ビヘイビアーをアタッチしている特定のリージョン用にエクスポートされたビューのみを取得します。

動作は、ブートストラップでシェルの領域に関連付けられます。

protected override IRegionBehaviorFactory ConfigureDefaultRegionBehaviors() { ViewModelInjectionBehavior.RegionsToAttachTo.Add(RegionNames.ElementViewRegion);

var behaviorFactory = base.ConfigureDefaultRegionBehaviors();
behaviorFactory.AddIfMissing("AutoPopulateExportedViewsBehavior", typeof(AutoPopulateExportedViewsBehavior));

}

これで、MEF と PRISM がどのように機能するかがわかると思います。

そして、まだ退屈していない場合: これは完璧です:

マイク・トーティのスクリーンキャスト

于 2013-04-04T07:27:37.600 に答える