この記事は
Prism Libarary のModule機能と、DIコンテナ(Prism.Unity)を併用する場合に
少し工夫(というほどでもないけど)が必要になったので、それを書き残すための記事
環境: .NET Core 3.1, Prism.Unity 8.0
具体的には
以下2つのやりたいことと解決するための方法を書く
1つ目
 やりたいこと:MainWindowViewModelにおいて、DIコンテナからモジュールのクラスを取得・利用したい
 解決方法:MainWindowViewModelのコンストラクタ内で、明示的にIModuleManager.LoadModule(使いたいモジュール)を呼び出す
2つ目
 やりたいこと:あるモジュールにおいて、他のモジュールのクラスを参照したクラスをDI登録したい
 解決方法:IModuleを継承したクラス内で、IContainerRegistry.GetContainer()したコンテナを使ってDI登録する。その際、メインプロジェクトのDependsOn(依存先)でモジュールを読み込む順番を明示する。
さっきから言っているモジュールとは
↓ 公式ドキュメントのモジュールに関するページ
検証用プロジェクトの構成
本題に入る前に、検証用ソリューションの説明 (PlantUMLを使ってみたかっただけ)
- 
Prism.Modularity.IModuleインターフェイスを実装したModule1~Module3がある
- メインプロジェクト(PrismModuleTest)で、すべてのモジュールをmoduleCatalog.AddModuleしている
- メインプロジェクトのMainWindowViewModelで、Module1がDI登録したクラスを使いたい- ↑ これがやりたいことの1つ目
 
- 
Module1ではModule3のクラスを使ったクラスをDI登録したい- ↑ これがやりたいことの2つ目
 
- 
Module2は比較のために置いてあるだけで、とくに使うことはない
いろいろ試してみたけど、図をきれいに整えることはできなかった・・・
MainWindowViewModelでDIコンテナからモジュールのクラスを取得・利用したい
まず、やりたことの1つ目について書く
本家GitHub↓の07-Modules - Codeを参考にした
つまずいたところ
メインプロジェクトで使ってもらうクラスをモジュール側で用意し、DI登録して
 public void RegisterTypes(IContainerRegistry containerRegistry)
 {
     containerRegistry.Register<IModuleClass, Module1Class>(nameof(Module1Class)); // DI登録
 }
メインプロジェクトApp.xaml.csでConfigureModuleCatalogメソッドを用いて
各モジュールを指定して (MainWindowViewModelでモジュール1を使う予定)
 public partial class App
 {
     protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
     {
         moduleCatalog.AddModule<Module1.Module1Module>(); // <- これを使う
         moduleCatalog.AddModule<Module2.Module2Module>();
         moduleCatalog.AddModule<Module3.Module3Module>();
     }
 }
MainWindowViewModelのコンストラクターの中で
メンバ変数である_module1ClassにDIコンテナからインスタンスを代入しようとしたところ
 public MainWindowViewModel(IContainerProvider container)
 {    
     _module1Class = container.Resolve<IModuleClass>("Module1Class");
 }
// エラーメッセージ
// Prism.Ioc.ContainerResolutionException:
// 'An unexpected error occurred while resolving 'InterfaceLibrary.IModuleClass', with the service name 'Module1Class''
「Resolveできませんよ~」的なエラーが出た
ログを確認すると
Appクラスのそれぞれメソッドが実行されたあと、すぐにMainWindowViewModelのコンストラクタが実行されている
つまり、DIコンテナにModule1がクラスを登録する前にResolveを試みてエラーになっていた
App # RegisterTypes
App # ConfigurationModuleCatalog
App # CreateShell
MainWindowViewModel # コンストラクター [開始]
例外がスローされました: 'Prism.Ioc.ContainerResolutionException' (Prism.Unity.Wpf.dll の中)
解決方法
MainWindowViewModelのコンストラクタでcontainer.Resolve()をする前に、モジュール側のDI登録を済ませればよい
DI登録を促すために、コンストラクタ内でLoadModuleを呼ぶ
そうすればModule1側のRegisterTypesが実行され、DI登録が完了した状態でMainWidowViewModelからResolveできる
-public MainWindowViewModel(IContainerProvider container)
+public MainWindowViewModel(IContainerProvider container, IModuleManager moduleManager)
 {
+    moduleManager.LoadModule(nameof(Module1.Module1Module)); // モジュールをマニュアルロードをしてから
     _m odule1Class = container.Resolve<IModuleClass>("Module1Class"); // Resolve する
 }
うまくいった場合のログ
MainWindowViewModelのコンストラクタの中でModule1がロードされている
- 
New !!はDI登録時ではなく、Resolveによりインスタンスが生成されたタイミングに出力されている
- モジュール2, 3 は登録のみで Resolveされずインスタンスが生成されていないので、New !!のログがない
App # RegisterTypes
App # ConfigurationModuleCatalog
App # CreateShell
MainWindowViewModel # コンストラクター [開始]
Module1Module # RegisterTypes
Module1Module # OnInitialized
_moduleManager_LoadModuleCompleted <-- Module1Module
Module1Class New !! // <-- MainWindowViewModelのメンバ変数に代入されたタイミング
MainWindowViewModel # コンストラクター [終了]
Module2Module # RegisterTypes
Module2Module # OnInitialized
_moduleManager_LoadModuleCompleted <-- Module2Module
Module3Module # RegisterTypes
Module3Module # OnInitialized
_moduleManager_LoadModuleCompleted <-- Module3Module
参考) モジュール1~3が有するクラス
    public class Module1Class : IModuleClass
    {
        public Module1Class()
        {
            Debug.WriteLine($"{nameof(Module1Class)} New !!");
        } // コンストラクタが呼ばれたら New !! を出力する
    }
// Module2, 3 も共通
イベントを登録しておくことで、いつモジュールがロードされたのかを監視している (↓参考)
余談
上記のようにマニュアル操作でモジュールをロードすることを明示したい場合は
Appクラスのほうも変更する
-moduleCatalog.AddModule<Module1.Module1Module>();
+moduleCatalog.AddModule<Module1.Module1Module>(InitializationMode.OnDemand);
// オンデマンドを引数に与える
今回はメインプロジェクトのウィンドウでモジュールを使うのでオンデマンド指定はしなかった
別ケースとして、後々にしか使わないモジュールについては
オンデマンドを指定することで、アプリの起動速度向上に寄与するかもしれない(?)
あるモジュールから他のモジュールのクラスを参照し、そのクラスをDI登録したい
つづいて、やりたいことの2つ目について書く
つまづいたことろの前に、小さくつまづいたこところ
モジュール内で、DIコンテナから何かインスタンスを引っ張り出して、それを使って登録したい
しかし、モジュール内の動きとしては**RegisterTypes(登録)が先に動き、そのあとOnInitialized(containerProvider:解決系)が動くため、「解決(DI Resolve)してから登録が」できない**
Module1Module # RegisterTypes
Module1Module # OnInitialized
これについては、正攻法ではないかもしれないが、登録用のcontainerRegistryをメンバ変数に格納しておいて
あとからOnInitializeでメンバ変数を使って登録作業をするとした
 public class Module1Module : IModule
 {
+    IUnityContainer _container;
     public void RegisterTypes(IContainerRegistry containerRegistry)
     {
+        _container = containerRegistry.GetContainer();
         containerRegistry.Register<IModuleClass, Module1Class>(nameof(Module1Class));
     }
 }
つまずいたところ本題
モジュール3のクラスをつかって、モジュール1側でなにかインスタンスをDI登録する
 public class Module1Module : IModule
 {
     IUnityContainer _container;
     public void OnInitialized(IContainerProvider containerProvider)
     {
+        // モジュール3のクラスのプロパティを使って
+        var module3property = containerProvider.Resolve<IModuleClass>("Module3Class").ModuleNumber;
+        // 何かのインスタンスをDI登録したい
+        _container.RegisterInstance<ISomeClass>("someclass", new SomeClass(module3property));
     }
     public void RegisterTypes(IContainerRegistry containerRegistry)
     {
         _container = containerRegistry.GetContainer(); // 登録するためのコンテナ
         containerRegistry.Register<IModuleClass, Module1Class>(nameof(Module1Class));
     }
 }
実行したら、先ほどと同じようなエラーが出た
Prism.Ioc.ContainerResolutionException: 'An unexpected error occurred while resolving 'InterfaceLibrary.IModuleClass', with the service name 'Module3Class''
「(モジュール1で)モジュール3を使いたいようだけど、モジュール3クラスの名前解決ができないよ」と。
解決方法
App.xaml.csでAddModuleするときに、モジュール同士の依存関係を明示する
モジュール1はモジュール3を使うので、dependsOn: nameof(Module3.Module3Module)とする
 public partial class App
 {
     protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
     {
-        moduleCatalog.AddModule<Module1.Module1Module>();
+        moduleCatalog.AddModule<Module1.Module1Module>(dependsOn: nameof(Module3.Module3Module))
         moduleCatalog.AddModule<Module2.Module2Module>();
         moduleCatalog.AddModule<Module3.Module3Module>();
     }
 }
うまくいった場合のログ
- モジュール3が先にロードされ、DI登録される。この時点ではインスタンスは生成されていない。
- モジュール1がロードされ、モジュール3のクラスを使ったときにインスタンスが生成されている (New !!)
- モジュール1の New !! はMainWindowViewModelのメンバ変数に代入されたときに表示される
- モジュール2は諸々が終わってからひっそりロードされる
App # RegisterTypes
App # ConfigurationModuleCatalog
App # CreateShell
MainWindowViewModel # コンストラクター [開始]
Module3Module # RegisterTypes // モジュール1がマニュアルロードされたので、それに先立ちモジュール3がロードされ始めた
Module3Module # OnInitialized
_moduleManager_LoadModuleCompleted <-- Module3Module
Module1Module # RegisterTypes
Module1Module # OnInitialized
Module3Class New !! // モジュール1で呼ばれた
_moduleManager_LoadModuleCompleted <-- Module1Module
Module1Class New !! // MainWindowViewModelで呼ばれた
MainWindowViewModel # コンストラクター [終了]
Module2Module # RegisterTypes
Module2Module # OnInitialized
_moduleManager_LoadModuleCompleted <-- Module2Module
おわりに
ポイントは2つ
- せっかちに使いたいモジュールは、使いたいタイミングでマニュアルロードすべし
- モジュール同士に依存関係がある場合は、カタログ登録時にメインプロジェクトに関係性をお知らせすべし
参考にさせていただいた記事