ホーム > コンピュータ > C# > WPF in C# > Prism

ベクトルドロー・レベルゼロ+

Prism Library 5.0 for WPFを使用しているコンポーネント間で依存関係を管理する[C# WPF Prism]

概要

MVVMパターン調べていて、Prismというツールキットを見つけました。MVVMパターンを使用するためのもっとも人気のあるツールキットのようです。

原文

訳文

Prismライブラリに配置されるアプリケーションは、潜在的に、多くの疎く結合した型とサービスから構成されている、複合アプリケーションです。 それらは、内容を提供するために相互作用する必要があります。そして、ユーザーアクションに基づく通知を受けます。 それらが、疎く結合しているため、それらは、必要なビジネス機能を提供するために、互いに相互作用と情報交換するための方法が必要です。 これら、さまざまな部分を互いに結びつけるために、Prismライブラリに配置されるアプリケーションは、依存関係注入コンテナに依存しています。

依存関係注入コンテナは、オブジェクトの間で依存関係の結合を減少させます。 クラスのインスタンスのインスタンスを生成するための能力を提供することによって、そして、コンテナの設定に基づいて、それらの寿命を管理します。 オブジェクトの作成時に、コンテナは、どんな依存関係でも注入します。その、オブジェクトは、その中に、必要です。 それらの依存関係が、まだ作成されていない場合、コンテナは、はじめに、それらの依存関係を作成し、解決します。 場合によっては、コンテナそのものは、依存関係として解決されます。 例えば、Unityアプリケーション・ブロック(Unity)を使用するとき、コンテナとして、モジュールは、コンテナを注入します。 それで、それらは、そのコンテナで、それらのビューとサービスを登録することができます。

コンテナを使用することのいくつかの利点があります。:

  • コンテナは、その依存関係を配置するための、それらの寿命を管理するための、コンポーネントの必要性を取り払います。
  • コンテナは、コンポーネントに影響を及ぼすことなく、実装された依存関係の交換を提供します。
  • コンテナは、モックアップが作成された依存関係を提供することで、検査を容易にします。
  • コンテナは、システムに簡単に追加される、新しいコンポーネントを提供することで、保守性を向上します。

Prismライブラリに基づくアプリケーションのコンテクストでは、コンテナには、特有の利点があります。

  • コンテナは、読み込まれるとき、モジュール依存関係を、モジュールに注入します。
  • コンテナは、登録とView ModelとViewを解決するために、使用されます。
  • コンテナは、View Modelを作成し、ビューを注入することができます。
  • コンテナは、領域マネージャとイベント・アグリゲータのような、構成サービスを注入します。
  • コンテナは、モジュール固有のサービスを登録するために使用されます。それは、モジュール固有の機能をもつサービスです。

備考

Prismの手引きのいくつかのサンプルは、コンテナとして、Unityアプリケーション・ブロック(Unity)に依存しています。 その他のコードサンプル、例えば、モジュール方式クイックスタートのために、Managed Add-in Framework(MAF)を使用します。そのものPrismライブラリは、コンテナ固有でありません。 そして、あなたは、Castle Windsor、StructureMapとSpring.NETのような、そのサービスと他のコンテナによるパターンを使用することができます。

重要な決定:依存関係注入コンテナを選択する

Key Decision: Choosing a Dependency Injection Container

Prismライブラリは、依存関係注入コンテナのために、2つのオプションを提供します。 :UnityやMEF。Prismは拡張可能です、それによって、少し操作で、他のコンテナを代わりに使用できます。 UnityとMEFは、依存関係注入のために、同じ基本的な機能を提供するにもかかわらず、それらは極めて異なる動作をします。 両方のコンテナで提供されるいくつかの機能には、次のものが含まれています。:

  • それら両方は、コンテナを持つ型を登録します。
  • それら両方は、コンテナを持つインスタンスを登録します。
  • それら両方は、いやおうなしに、登録された型のインスタンスを作成します。
  • それら両方は、コンストラクタに登録された型のインスタンスを注入します。
  • それら両方は、プロパティに登録された型のインスタンスを注入します。
  • それら両方は、管理する必要がある型と依存関係を記録するための、宣言型の属性を持ちます。
  • それら両方は、オブジェクト・グラフ内の依存関係を解決します。

Unityは、MEFにはない、いくつかの機能を提供します。:

  • それは、登録なしで、具体的な型を解決します。
  • それは、開いたジェネリックを解決します。
  • それは、呼び出すオブジェクトを捕えるため、ターゲットオブジェクトに追加の機能を追加するために、横取りしています。

MEFは、Unityにはない、いくつかの機能を提供します。:

  • それは、ディレクトリ内のアセンブリを検出します。
  • それは、XAPファイルのダウンロードとアセンブリの検出を使用しています。
  • それは、新しい型が発見されるように、プロパティとコレクションを再構成します。
  • それは、自動的に、派生型をエクスポートします。
  • それは、.NET Frameworkで配布されます。

コンテナには、機能の違いがあり、異なる動作をします。 しかし、Prismライブラリは、どちらのコンテナでも動作し、同じような機能を提供するでしょう。 どのコンテナを使用するべきか、考えるとき、あなたの筋書きに、うまく収まる、先程の機能と発見を覚えておいてください。

コンテナを使用するための注意事項

Considerations for Using the Container

あなたは、コンテナを使用する前に、次の事を考慮する必要があります。:

  • コンポーネントの寿命が、シングルトン、あるいは、インスタンスとして登録する必要があるか考えてみてください。:
    • コンポーネントが、グローバル・サービスの場合、それは、ログ記録サービスのような、一つのリソースのためのリソースマネージャとして機能します。
    • コンポーネントが、複数の利用者に共用状態を提供する場合、シングルトンとして、それを登録するといいかもしれません。
    • 注入されたオブジェクトが、依存したオブジェクトが必要となるたびに、それを注入した、新しいインスタンスを持っておく必要がある場合、シングルトンとして、それを登録するといいかもしれません。
    • 例えば、それぞれのビューは、おそらく、View Modelの新しいインスタンスを必要とする場合、それを非シングルトンとして、登録します。
  • あなたが、コードや設定を通して、コンテナを設定したいかどうかについて考えてみてください。:
    • あなたが、中央で、すべての異なるサービスを管理したい場合、設定を通してコンテナを設定します。
    • あなたが、条件つきで特定のサービスを登録したい場合、コードを通してコンテナを設定します。
    • あなたが、モジュール・レベルのサービスを使用している場合、コードを通してコンテナを設定することについて考えてみてください。それで、それらのサービスは、モジュールが読み込まれる場合だけ、登録されています。

備考

MEFのような、いくつかのコンテナでは、設定ファイルによって設定できません。コードによって設定する必要があります。

中心となる筋書き

Core Scenarios

コンテナは、2つの主要な目的、すなわち、登録と解像するために使用されています。

登録

Registering

あなたが、オブジェクトに依存関係を注入する前に、依存関係の種類は、コンテナで登録されている必要があります。 一般的に種類を登録することは、そのインターフェイスを実装しているコンテナにインターフェースと具体的な種類を渡すことが含まれています。 登録する種類とオブジェクトのために、主に2つの手段があります。:コードを通して、または、設定を通して、具体的な手段は、コンテナからコンテナに変化します。

一般的に、コードを通してコンテナに登録する種類とオブジェクトの2つの方法があります。:

  • あなたは、コンテナで種類やマッピングを登録することができます。適切なタイミングで、コンテナは、あなたが指定する種類のインスタンスを構築します。
  • あなたは、シングルトンとして、コンテナで既存のオブジェクト・インスタンスを登録することができます。コンテナは、既存のオブジェクトに参照を返します。

Unityコンテナによる型の登録

Registering Types with the Unity Container

初期化の間、ビューとサービスのような、種類は、他の種類を登録することができます。 登録は、コンテナを通して、それらの依存関係を提供します。そして、それらは、他の種類からアクセスできます。 これを実行するには、種類は、コンテナをモジュール・コンストラクタに注入しておく必要があります。 次のコードは、どのように、CommandingクイックスタートのOrderModule型が形式を登録するかを示します。

あなたが、どのコンテナを使用するかに応じて、また、登録は、設定を通して、コードの外側で、実行されることができます。 この例については、モジュール・アプリケーション開発において設定ファイルを使用する登録モジュールを参照してください。


// OrderModule.cs
public class OrderModule : IModule
{
    public void Initialize()
    {
        this.container.RegisterType<IOrdersRepository, OrdersRepository>(new ContainerControlledLifetimeManager());
        ...
    }
    ...
}

備考

設定と比較して、コードの中で登録する利点は、モジュールが読み込まれる場合にだけ、登録が発生するところにあります。

MEFによる型の登録

Registering Types with MEF

MEFは、コンテナと一緒に、登録する型のために、属性に基づくシステムを使用します。 その結果、コンテナに、型の登録を追加することは、単純です:それは、次のコードの例で示す型へ[エクスポート]属性の追加が必要です。


[Export(typeof(ILoggerFacade))]
public class CallbackLogger: ILoggerFacade
{
}

MEFを使用するときの他のオプションは、クラスのインスタンスを作成することです。そして、その特定のインスタンスをコンテナに登録します。 ここに、示されるように、MEFクイックスタートによるモジュラー性のQuickStartBootstrapperは、ConfigureContainerメソッドで、この例を示します。:


protected override void ConfigureContainer()
{
    base.ConfigureContainer();

    // Because we created the CallbackLogger and it needs to 
    // be used immediately, we compose it to satisfy any imports it has.
    // 私たちは、CallbackLoggerを作成し、そして、それが、すぐに使用する必要があるため、
    // 私たちは、それがもつ、どんなインポートでも満足させるために、それを構成します。
    this.Container.ComposeExportedValue(this.callbackLogger);
}

私たちは、CallbackLoggerを作成し、そして、それが、すぐに使用する必要があるため、我々は、それがもつ、どんなインポートでも満足させるために、それを構成します。

備考

あなたのコンテナとして、MEFを使用するとき、あなたが、型を登録するために、属性を使用することをお勧めします。

解決

Resolving

型が登録されたあと、それは、依存関係として解決、あるいは、注入されることができます。 型が解決されている、そして、コンテナが、新しいインスタンスを作成する必要があるとき、それは、これらのインスタンスに依存関係を注入します。

  • 一般に、型が解決されたとき、3つのうち、1つが起こります。:
  • 型が登録されなかった場合、コンテナは、例外を投げます。

備考

いくつかのコンテナでは、Unityが、含まれています。あなたが、登録されなかった、具体的な型を解決することができます。

  • 型が、シングルトンとして登録された場合、コンテナは、シングルトン・インスタンスを返します。 初めて型が呼び出された場合、コンテナは、それを作成します。そして、今後の呼び出しのために、それを保ち続けます。
  • 型が、シングルトンとして登録されなかった場合、コンテナは、新しいインスタンスを返します。

備考

既定では、MEFで登録される型は、シングルトンです。そして、コンテナは、オブジェクトに参照を格納します。 Unityでは、オブジェクトの新しいインスタンスは、デフォルトで返されます。そして、コンテナは、オブジェクトに参照を保持しません。

Unityでインスタンスを解決する

Resolving Instances with Unity

Commanding QuickStartから、次のコードの例は、その場所でOrdersEditorViewを示します。 そして、OrdersToolBarビューは、コンテナから、対応する領域のそれらの関連付けまで解決されます。


// OrderModule.cs
public class OrderModule : IModule
{
    public void Initialize()
    {
        this.container.RegisterType<IOrdersRepository, OrdersRepository>(new ContainerControlledLifetimeManager());

        // Show the Orders Editor view in the shell's main region.
        // シェルの中心となる領域内の注文エディタ・ビューを表示します。
        this.regionManager.RegisterViewWithRegion("MainRegion",
                            () => this.container.Resolve<OrdersEditorView>());

        // Show the Orders Toolbar view in the shell's toolbar region.
          // シェルのツールバー領域内の注文ツールバー・ビューを表示します。
        this.regionManager.RegisterViewWithRegion("GlobalCommandsRegion",
                            () => this.container.Resolve<OrdersToolBar>());
    }
    ...
}

OrdersEditorViewModelコンストラクタには、次に示す依存関係(注文リポジトリと注文コマンド・プロキシ)が含まれています。それが解決されるとき、それは注入されます。


// OrdersEditorViewModel.cs
public OrdersEditorViewModel(IOrdersRepository ordersRepository, OrdersCommandProxy commandProxy)
{
    this.ordersRepository = ordersRepository;
    this.commandProxy     = commandProxy;

    // Create dummy order data.
    // ダミー注文データを作成します。
    this.PopulateOrders();

    // Initialize a CollectionView for the underlying Orders collection.
    //基盤となるOrdersコレクションのためのCollectionViewを初期化します。
    this.Orders = new ListCollectionView( _orders );
    // Track the current selection.
    //現在の選択した内容を追跡します。
    this.Orders.CurrentChanged += SelectedOrderChanged;
    this.Orders.MoveCurrentTo(null);
}

前述のコードで示される、コンストラクタ注入に加えて、また、Unityは、プロパティ注入のための提供します。 オブジェクトが解決されるとき、[Dependency] 属性を適用する、どんなプロパティも、自動的に解決され、注入されます。

MEFでインスタンスを解決する

Resolving Instances with MEF

次に示すコードの例は、モジュラー性のMEFクイックスタート内のブートストラッパーが、どのように、シェルのインスタンスを得るかを示します。 具体的な型を要求する代わりに、コードは、インターフェースのインスタンスを要求することができました。


protected override DependencyObject CreateShell()
{
    return this.Container.GetExportedValue();
}

MEFで、解決されるどんなクラスででも、また、MEFクイックスタートのモジュラー性のModuleAから、次のコードの例で示すように、 あなたは、注入されたILoggerFacadeとIModuleTrackerを持っている、コンストラクタ注入を使用することができます。


[ImportingConstructor]
public ModuleA(ILoggerFacade logger, IModuleTracker moduleTracker)
{
    if (logger == null)
    {
        throw new ArgumentNullException("logger");
    }
    if (moduleTracker == null)
    {
        throw new ArgumentNullException("moduleTracker");
    }
    this.logger = logger;
    this.moduleTracker = moduleTracker;
    this.moduleTracker.RecordModuleConstructed(WellKnownModuleNames.ModuleA);
}

他のオプションは、プロパティ注入を使用することです。MEFクイックスタートとモジュラー性から、ModuleTrackerクラスで示すように、 それは、注入されたILoggerFacadeのインスタンスを持っています。


[Export(typeof(IModuleTracker))]
public class ModuleTracker : IModuleTracker
{
     [Import] private ILoggerFacade Logger;
}

Prismで、依存関係注入コンテナとサービスを使用する

Using Dependency Injection Containers and Services in Prism

多くの場合、適切な「コンテナ」として参照される、依存関係注入コンテナは、コンポーネントの間で、依存関係を満たすために使用されます。 ;一般的に、これらの依存関係を満足させることは、登録と解決が含まれています。 Prismライブラリは、Unityコンテナのための、そして、MEFのためのサポートを提供します。 しかし、それは、コンテナ固有でありません。ライブラリが、IServiceLocatorインターフェースにより、コンテナにアクセスするため、コンテナは、置き換えることができます。 これを実行するには、あなたのコンテナは、IServiceLocatorインターフェースを実装している必要があります。 通常、あなたが、コンテナを置き換えている場合、また、あなたは、あなた独自のコンテナ固有のブートストラッパーを提供する必要があります。 IServiceLocatorインターフェースは、Common Service Locator Libraryで定義されます。 これは、依存関係注入コンテナやサービス・ロケーターのような、IoC(制御の反転)コンテナの上に抽象化を提供するための、オープンソースの取り組みです。 このライブラリを使用する目的は、特定の実装に結びつけずに、IoCとサービスの場所を活用することです。

Prismライブラリは、UnityServiceLocatorAdapterとMefServiceLocatorAdapterを提供します。 両方のアダプタは、ServiceLocatorImplBase型を拡張することによって、ISeviceLocatorインターフェースを実装します。 次に示す図は、クラス階層を示しています。

クラス階層

Prismで共通のサービス・ロケーターの実装

The Common Service Locator implementations in Prism

Prismライブラリは、特定のコンテナを参照したり、依存したりしませんが、アプリケーションが、特定のコンテナに頼ることが典型的です。 これは、特定のアプリケーションが、コンテナを参照することが、合理的なことを示していますが、Prismライブラリは、直接、コンテナを参照しません。 例えば、株トレーダーRIといくつかのクイックスタートは、Prismは、Unityを頼るコンテナとして、含まれています。他のサンプルとクイックスタートは、MEFをあてにしています。

IServiceLocator

IServiceLocator

次に示すコードは、IServiceLocatorインターフェースを示しています。


public interface IServiceLocator : IServiceProvider
{
    object GetInstance(Type serviceType);
    object GetInstance(Type serviceType, string key);
    IEnumerable<object> GetAllInstances(Type serviceType);
    TService GetInstance<TService>();
    TService GetInstance<TService>(string key);
    IEnumerable<TService> GetAllInstances<TService>();
}

サービス・ロケーターは、次のコードで拡張メソッドを示し、Prismライブラリで拡張されます。 IServiceLocatorが解決することのためのただ一つの使用されるのを、あなたは見ることができます。 それを表すことは、インスタンスを得るために使用されます;それは、使用されたのための登録でありません。


// ServiceLocatorExtensions
public static class ServiceLocatorExtensions
{
    public static object TryResolve(this IServiceLocator locator, Type type)
    {
        try
        {
            return locator.GetInstance(type);
        }
        catch (ActivationException)
        {
            return null;
        }
    }

    public static T TryResolve<T>(this IServiceLocator locator) where T: class
    {
        return locator.TryResolve(typeof(T)) as T;
    }
}

UnityコンテナのTryResolve拡張メソッドは、それが、登録されている場合、解決されている型のインスタンスを返すことをサポートしません。 ;それ以外の場合には、それは、nullを返します。

次のコードの例に示すように、ModuleInitializerは、モジュールを読み込む間、モジュールを解決するために、IServiceLocatorを使用します。


// ModuleInitializer.cs - Initialize()
IModule moduleInstance = null;
try
{
    moduleInstance = this.CreateModule(moduleInfo);
    moduleInstance.Initialize();
}
...

// ModuleInitializer.cs - CreateModule()
protected virtual IModule CreateModule(string typeName)
{
    Type moduleType = Type.GetType(typeName);
    if (moduleType == null)
    {
        throw new ModuleInitializeException(string.Format(CultureInfo.CurrentCulture, Properties.Resources.FailedToGetType, typeName));
    }

    return (IModule)this.serviceLocator.GetInstance(moduleType);
}

IServiceLocatorを使用するための注意事項

Considerations for Using IServiceLocator

IServiceLocatorは、汎用コンテナであることを示すものではありません。コンテナは、使用の異なる意味を持っています。 それは、多くの場合、そのコンテナが選ばれる理由のために、決定を駆動します。 この考えを持ち込み、株トレーダーRIは、IServiceLocatorを使用する代わりに、依存関係注入コンテナ・ディレクトリを使用します。 これは、あなたのアプリケーションの開発のために推薦される方法です。

次の状況では、あなたは、IServiceLocatorを使用するために、適切な場合があります。:

  • あなたは、複数のコンテナをサポートする必要がある、サードパーティー・サービスを設計している、独立したソフトウェア・ベンダー(ISV)です。
  • あなたは、複数のコンテナを使用する場所で、組織で使用するサービスを設計しています。

詳細情報

More Information

コンテナに関連する情報については、以下を参照してください。:

Copyright (C) 2011-2016 kukekko All Rights Reserved.
kukekko@gmail.com
ご連絡の際はアドレスの@は半角にしてください。 また、お問い合わせページのURLの明記をお願いします。
「掲載内容は私自身の見解であり、所属する組織を代表するものではありません 」。
inserted by FC2 system