はい、そうすべきです。モジュールをインターフェイスではなく独自の実装タイプとしてエクスポートする場合、モジュールをインポートする消費モジュールは、モジュールの実装を含むライブラリを参照する必要があります。IoC を使用する主な理由の 1 つは、これを回避することです。
はい、1 つのコンテナが必要です。モジュールがコンテナーを保持している場合、コンテナーが存在する前にモジュールのインスタンスが必要なため、このモジュールをエクスポート/インポートすることはできません。ここには実際の MEF 固有の問題はありません。Unity などと同じです。PRISM アプリの場合、モジュールのインスタンス化と配線を 1 つの場所 (コンテナー) で分離するという考え方です。コンテナーは Bootstrapper で何よりも先に作成され、シェル、モジュール、サービスなど、必要なものがすべて作成されます。オブジェクトのインスタンス化と参照をまったく異なるコンテキストで管理するアプリケーション内に他の IoC コンテナーを用意することは理にかなっています。たとえば、UI 用ではなく、複雑なビジネス オブジェクトを結び付けるためです。また、それはメイン コンテナーが認識しない内部 (プライベート) コンテナーで MEF を使用してモジュール自体を構築することは理にかなっています。それ自体が複合UIであるモジュールを備えた複合 UIがあるよりも。本当に必要な場合はよく考えてください。たとえば、アセンブリを2回ロードするなど、この種の問題に簡単に遭遇します。
前と同じように。モジュール B は、ModuleA のインターフェイス プロジェクトを参照し、タイプ IModuleA のフィールドまたはパラメーターをインポートします。コンテナーは依存関係を解決して ModuleA を注入します。
前に述べたように、アーキテクチャをまっすぐにする必要があります。モジュール間に依存関係を注入する場合は、それらが同じコンテナー内にある必要があります。それがIoCの考え方です。
複数の IoC コンテナーを使用する複雑なアプリケーションに取り組んでいます。UI には MEF を使用しています。これは、シェルといくつかの UI モジュールです。ビジネス ロジックに関連するものについては、AutoFac IoC コンテナーを使用します。主に、Autofac は「実際の」IoC コンテナーであり、MEF はそうではないためですが、はるかに高速であるためでもあります。Autofac は、MEF でできることは何でも実行できます。次回は、UI にも MEF の代わりに Autofac を使用します。
質問にはたくさんの質問があります....
そして、これは私が少し前に出した同様の質問に対する答えです。うまくいけばあなたにも役立ちます:
ここでは原則としてシステムを説明することしかできませんが、正しい方向に向けられるかもしれません。何に対しても常に数多くのアプローチがありますが、これが私がベスト プラクティスであると理解したものであり、私が非常に良い経験をしたものです。
ブートストラップ
MefBootstrapper
Prism や 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
、すべてのビューがUserControl
MEF コンテナーとして登録されます。
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 がどのように機能するかがわかると思います。
そして、まだ退屈していない場合: これは完璧です:
マイク・トーティのスクリーンキャスト