WPF+PrismでMessagePipeを利用する
高速メッセージングライブラリMessagePipe。同じメッセージングシステムはPrismにもEventAggregator
クラスとして提供されていますが、それよりもはるかに高速軽量であるとうたわれています。
ただ個人的にはそもそもPrismのEventAggregator
はほとんど使ったことがなく、実際にはRxのSubject
のほうをもっぱら使っています。WPFだと結局通常のevent
ハンドリングや、ReactivePropertyを組み合わせることが多いことから、Rxで統一するのが簡単ですし…
さてMessagePipeの大きな利点ですが高速軽量なだけではありません。名前付きパイプやTCP/UDPを利用して、IPC機能も提供されている点も見逃せません。デスクトップアプリ作っていてもちょろっとIPCしたいなーというときに便利そうです。
よしじゃあこれをPrismに統合して使ってみよう!と思い立ちちょろちょろと奮闘したのでその記録を残しておきます。
適当なWPFプロジェクトを作成し、さっそくPrismとMessagePipeをインストします。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MessagePipe" Version="1.7.2" />
<PackageReference Include="Prism.DryIoc" Version="8.1.97" />
</ItemGroup>
</Project>
PrismにはDIが統合されていますが、そのDIコンテナのバックエンドはUnityかDryIocから選択することができます。Unityは数か月前にアーカイブ化されてメンテが終了してしまったため、実質DryIoc一択です。
さああとはコンテナに型やらインスタンスを登録するだけですね。MessagePipeはMicrosoft.Extensions.DependencyInjectionを前提としたDI登録用APIを持ちます。当然DryIocにはそのまま適用できないので、ServiceCollectionExtensions.AddMessagePipeの実装をカンニングして型を登録しちゃいましょう。これで完成ですね。
internal!!!!!
はい。 というわけで何とかしましょう。
実はDryIocにはMicrosoft.Extensions.DependencyInjectionへの変換や互換APIを提供するDryIoc.Microsoft.DependencyInjectionというライブラリが公開されています。なので、
-
ServiceCollection
のインスタンス作成 - MessagePipeを登録
- DryIocに変換してPrismに渡す
この手順でいけるはずです。
さっそくパッケージを追加して…コネコネすれば…
<ItemGroup>
<PackageReference Include="DryIoc.Microsoft.DependencyInjection" Version="5.1.0" />
<PackageReference Include="MessagePipe" Version="1.7.2" />
<PackageReference Include="Prism.DryIoc" Version="8.1.97" />
</ItemGroup>
いけました。出来上がったのがこちらです。
using System;
using System.Collections.Generic;
using System.Windows;
using Microsoft.Extensions.DependencyInjection;
using DryIoc;
using DryIoc.Microsoft.DependencyInjection;
using Prism.Ioc;
using Prism.DryIoc;
using MessagePipe;
public partial class App
{
private Container ConvertToDryIocContainer(IEnumerable<ServiceDescriptor> services, Func<IRegistrator, ServiceDescriptor, bool>? registerService = null)
{
var rules = this.CreateContainerRules();
var container = new Container(rules);
container.Use<IServiceScopeFactory>(r => new DryIocServiceScopeFactory(r));
container.Populate(services, registerService);
return container;
}
protected override IContainerExtension CreateContainerExtension()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddMessagePipe();
var container = this.ConvertToDryIocContainer(serviceCollection);
var ext = new DryIocContainerExtension(container);
return ext;
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
}
protected override Window CreateShell()
=> this.Container.Resolve<MainWindow>();
}
結局DryIoc.Microsoft.DependencyInjection
が提供するDryIocへの変換メソッドでは、Rules
を指定することができなかったため、そこの実装だけ自前で実装する形で対応しました。
CreateContainerExtension
メソッドは名前の通りDIコンテナのバックエンドインスタンスを生成することだけが目的のメソッドなので、本来ここで型登録をするのはお作法がよろしくないのですが…
ServiceCollection
にアクセスできるポイントがここしかないので、なにとぞご容赦を…
何はともあれこれでWPF+Prismな環境でMessagePipeを使えるようになりました。
PrismがGenericHostでも動くようになってくれれば苦労しないんですがねぇ…
Discussion