Maui: [仕様] Microsoft.Extensions.Hostingおよび/またはMicrosoft.Extensions.DependencyInjection

作成日 2020年05月18日  ·  21コメント  ·  ソース: dotnet/maui

Microsoft.Extensions.Hostingの機能を.NETMAUIに焼き付けます

https://montemagno.com/add-asp-net-cores-dependency-injection-into-xamarin-apps-with-hostbuilder/

.netcore3.0でセットアップされた汎用ホスト構造を利用して.NETMAUIアプリケーションを初期化します。

これにより、ユーザーに非常にMicrosoftのエクスペリエンスが提供され、ASP.NETコアに沿った多くのコードが提供されます。

IServiceProviderを.NETMAUIに深くルートします

Activator.CreateInstance(Type)のすべてのインスタンスをIServiceProvider.Get()に置き換えます

たとえば、ElementTemplateでこれを変更した場合
https://github.com/dotnet/maui/blob/1a380f3c1ddd9ba76d1146bb9f806a6ed150d486/Xamarin.Forms.Core/ElementTemplate.cs#L26

次に、typeを介して指定されたDataTemplateは、コンストラクターインジェクションを介して作成されることを利用します。

`` `C#
Host.CreateDefaultBuilder()
.ConfigureHostConfiguration(c =>
{{
c.AddCommandLine(new string [] {$ "ContentRoot = {FileSystem.AppDataDirectory}"});
c.AddJsonFile(fullConfig);
})
.ConfigureServices((c、x)=>
{{
nativeConfigureServices(c、x);
ConfigureServices(c、x);
})
.ConfigureLogging(l => l.AddConsole(o =>
{{
o.DisableColors = true;
}))
。建てる();

static void ConfigureServices(HostBuilderContext ctx、IServiceCollectionサービス)
{{
if(ctx.HostingEnvironment.IsDevelopment())
{{
var world = ctx.Configuration ["Hello"];
}

services.AddHttpClient();
services.AddTransient<IMainViewModel, MainViewModel>();
services.AddTransient<MainPage>();
services.AddSingleton<App>();

}

### Shell Examples

Shell is already string based and just uses types to create everything so we can easily hook into DataTemplates and provide ServiceCollection extensions 

```C#
static void ConfigureServices(HostBuilderContext ctx, IServiceCollection services)
{
     services.RegisterRoute(typeof(MainPage));
     services.RegisterRoute(typeof(SecondPage));
}

すべてのDataTemplatesがIServiceProviderを介して接続されている場合、ユーザーはDataTemplatesでインターフェイスを指定できます。

<ShellContent ContentTemplate="{DataTemplate view:MainPage}"/ShellContent>
<ShellContent ContentTemplate="{DataTemplate view:ISecondPage}"></ShellContent>

コンストラクタインジェクションでベイク

`` `C#
パブリッククラスアプリ
{{
public App()
{{
InitializeComponent();
MainPage = ServiceProvider.GetService();
}
}
パブリック部分クラスMainPage:ContentPage
{{
public MainPage(IMainViewModel viewModel)
{{
InitializeComponent();
BindingContext = viewModel;
}
}

パブリッククラスMainViewModel
{{
public MainViewModel(ILoggerロガー、IHttpClientFactory httpClientFactory)
{{
var httpClient = httpClientFactory.CreateClient();
logger.LogCritical( "常にログに記録します!");
Hello = "Hello from IoC";
}
}

### This will allow Shell to also have baked in Constructor Injection

```C#
Routing.RegisterRoute("MainPage", MainPage)

GotoAsync("MainPage") // this will use the ServiceProvider to create the type

シェルの一部として指定されたすべてのContentTemplatesは、IServiceProviderを介して作成されます

    <ShellContent
        x:Name="login"
        ContentTemplate="{DataTemplate MainPage}"
        Route="login" />

考慮すべき実装の詳細

Microsoft.Buildを使用して、起動パイプラインを容易にします

ホスト機能をプルして、物事が登録されている特定のスタートアップの場所を明確にします
https://montemagno.com/add-asp-net-cores-dependency-injection-into-xamarin-apps-with-hostbuilder/

これには、すでにasp.netコアに対して機能するIoCコンテナーの実装に結び付けることができるという利点があります。

長所:これにより、.NET開発者は一貫したエクスペリエンスを得ることができます。
短所:パフォーマンス? これはモバイルにとってやり過ぎですか?

DIコンテナオプション

Microsoft.Extensions.DependencyInjectionを優先してDependencyServiceを廃止する

Xamarin.Formsには現在、多くの機能を備えていない非常にシンプルな自家製の依存関係サービスがあります。 すでに利用可能なオプションに直面してこのサービスの機能を拡張することは、あまり意味がありません。 他のMicrosoftプラットフォームとより適切に連携するために、 Microsoft.Extensions.DependencyInjection内のコンテナーに切り替えることができます。

DependencyServiceの自動登録は、新しいレジストラに関連付けられます。 ユーザーがレジストラにアセンブリスキャンを実行することを選択した場合、これによりDependencyServiceがトリガーされてアセンブリレベルの属性がスキャンされます

このコンテナを使用する際の注意点の1つは、アプリの起動後にタイプをその場で登録できないことです。 起動プロセスの一部としてのみタイプを登録でき、IServicePRoviderが構築されるとそれだけです。 営業登録は締め切らせていただきました

長所:フル機能のコンテナです
短所:パフォーマンス?

DependencyServiceを変換して、IServiceCollectionを内部コンテナーとして使用し、IServiceProviderを実装するようにします

これにより、人々が最高のパフォーマンスを求めているだけの場合は、非常にスリム化された機能のないコンテナーを使用できます。 おそらくこれをデフォルトとして使用することができ、その後、必要に応じて、より機能的なものを選択することができます。

`` `C#
パブリッククラスDependencyService:IServiceProvider
{{
}

public static ServiceCollectionExtensions
{{
public static DependencyService Create(this IServiceCollection);
}
`` `

考慮事項

これは、新しいユーザーのアプリエクスペリエンスに全体的に役立ちますか? 新しいユーザーのビルドホストの起動ループを理解するためのオーバーヘッドを本当に追加したいですか? Initを使用するだけのデフォルト設定を設定しておくと、新しいユーザーは設定ファイルやconfigureservicesなどを設定しなくても必要なことを簡単に実行できるので便利です。

パフォーマンス

私のテスト限定テストでは、MicrosoftHostingビットが起動するのに約25ミリ秒かかります。 おそらく、25ミリ秒をさらに深く掘り下げて、それを回避できるかどうか、またはそのコストがすでに発生する別の起動コストの一部であるかどうかを確認する必要があります。

下位互換性

  • 現在のDependencyServiceは、アセンブリレベルの属性をスキャンします。これは、.NETMAUIのオプトインに移行する可能性が最も高いものです。 デフォルトでは、サービスコレクションを介して明示的に登録する必要があります

難易度:中/大

既存の作業:
https://github.com/xamarin/Xamarin.Forms/pull/8220

breaking proposal-open

最も参考になるコメント

これを実装する際に考慮すべきことは、マウイ島の実装全体の主な依存関係としてMicrosoft.Extensions.DependencyInjection.Abstractionsプロジェクトを使用することです。 Microsoft.Extensions.DependencyInjectionのフットプリントをコンテナの作成にのみ使用するように減らすことで、サードパーティのライブラリとフレームワークがコンテナに依存しないようになります。

これにより、開発者またはフレームワークは、Mauiが提供するものとは異なるコンテナーを使用するオプションを使用できます。 抽象化プロジェクトを使用することにより、開発者またはフレームワークに必要な作業は、抽象化プロジェクトにインターフェースを実装するだけです。 すべてのマウイコードがインターフェイスを使用する場合。 これにより、プラットフォームでの依存性注入の動作を再検討する際に、すべての人に大規模な拡張ポイントが提供されます。

全てのコメント21件

これを実装する際に考慮すべきことは、マウイ島の実装全体の主な依存関係としてMicrosoft.Extensions.DependencyInjection.Abstractionsプロジェクトを使用することです。 Microsoft.Extensions.DependencyInjectionのフットプリントをコンテナの作成にのみ使用するように減らすことで、サードパーティのライブラリとフレームワークがコンテナに依存しないようになります。

これにより、開発者またはフレームワークは、Mauiが提供するものとは異なるコンテナーを使用するオプションを使用できます。 抽象化プロジェクトを使用することにより、開発者またはフレームワークに必要な作業は、抽象化プロジェクトにインターフェースを実装するだけです。 すべてのマウイコードがインターフェイスを使用する場合。 これにより、プラットフォームでの依存性注入の動作を再検討する際に、すべての人に大規模な拡張ポイントが提供されます。

@ahoeflingうん、それがマウイがすべてを消費する方法です。 唯一の問題は、デフォルトの実装にすでに実装されているコンテナを使用する必要があるのか​​、それともパフォーマンスに関してもう少しモバイル志向の独自のコンテナをロールする必要があるのか​​ということです。

@PureWeen現在のデフォルトのコンテナ実装は、モバイルでうまく機能します。 しかし、hostbuilderがもたらす依存関係の塊は驚異的です。

@PureWeenこれは聞いて素晴らしいです!

私が理解している限り、拡張プロジェクトのコンテナのパフォーマンスはかなり安定しています。 デフォルトのコンテナーから開始することをお勧めします。パフォーマンステストが期待を満たさない場合は、繰り返すことができます。 いくつかの拡張ポイントを処理するために考えていたものの小さなコードサンプルをまとめました。

これにより、別のコンテナーを使用する場合、開発者、フレームワーク、またはMauiプラットフォームでコンテナーを簡単に交換できます。

たぶん、 System.Maui.Applicationは次のようになります。
`` `c#
パブリッククラスアプリケーション:Element、IResourcesProvider、IApplicationController、IElementConfiguration
{{
public IServiceProvider Container {get; }

protected virtual IServiceProvider CreateContainer()
{
    var serviceCollection = new ServiceCollection();
    RegisterDependencies(serviceCollection);
    return serviceCollection.BuildServiceProvider();
}

// This should be implemented in the app code
protected virtual void RegisterDependencies(IServiceCollection serviceCollection)
{
    // TODO - Register dependencies in app code
}

public Application()
{
    Container = CreateContainer();

    // omited code
}

// omitted code

}
`` `

@ahoeflingこれを使用して、

https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.hostbuilder.useserviceproviderfactory?view=dotnet-plat-ext-3.1

AutoFacのようなものがフックするものです

私が理解している限り、Host Builderは、すぐにログアウトするなど、デフォルトの依存関係を追加するための豊富なAPIを提供します。 私の提案したコードがはるかに無駄のないところ。 どちらのアプローチも、依存性注入のさまざまな問題を解決するため、他のアプローチよりも「正しい」とは思いません。

ホストビルダー
コンソールロギングが特定の方法で行われ、HttpClientFactoryが特定の方法で機能するという意見を作成したい場合は、ホストビルダーがおそらく最善の選択肢になるでしょう。

リーンサービスコレクション
DIシステムを可能な限りスリムにし、アプリ開発者に何が機能し、何が機能しないかを決定させる場合は、提案されたコードなどがそれを実装する方法になると思います。

私が問題を見ている方法は、 DependencyServiceを交換するための最も侵襲性の低い方法です。 最終目標がasp.netコアとホストビルダーモデルで適切なパターンに従うことである場合、ホストビルダーを利用するStartup.csクラスを作成するのがおそらく最善でしょう。

OPのコードは、 ServiceProvider.GetService<MainPage>()どのように機能し、 MainViewModelがどのように検出されるかが関係するタイプからは不明であるため、問題があります。 また、NRT警告をトリガーするnullも含まれます。 これはすべて、型システムを回避する方法のようです。 ASP.Netは、クリーンなAPIの輝かしい例ではありません。

ユーザーが現在のようにこの機能の使用を避け、タイプセーフな方法でオブジェクトを指定できるようにすることが重要です。 そして、それを回避することで、ユーザーはパフォーマンスのペナルティを支払うことも回避できます。

type App() =
    inherit Application()
    let vm = someMainViewModel(...)
    let mainPage = MainPage(vm)
    do  base.MainPage <- mainPage

人々がリフレクション/コンベンション/アノテーションベースの方法でビューモデルを取得したい場合、MAUIは本当に明示的なサポートを提供する必要がありますか? とにかく彼らはこれを行うことができませんか? 元のリンクは、できることを示唆しています。

@ahoefling

ホストビルダーとリーンサービスコレクション

そうです、一方と他方の利点を理解することは間違いなくこれの大きな部分です!

最初はリーンサービスコレクションを見ていましたが、オールインすることにしました。
ビルダーの方向性を使用する利点の一部は、既存のネットコアの実装に結びつくだけであるということです。 たとえば、AutoFacを使用すると、独自のものを発明するのではなく、同じ起動ループ構文を使用できます。 ドメイン知識は譲渡可能です。

@PureWeenこれは理にかなっており、

すべてのスタートアップコードを処理するApp.csまたはApp.xaml.csすぐ隣にあるStartup.csを作成する必要があると思います。 これにより、マウイ島の依存性注入ストーリーが.NETエコシステムの他のプロジェクトと統合されます。 昨年DNNモジュールに依存性注入を実装したとき、開発者がスキルを非常に簡単に移転できるようにする同様のことを行いました。

すべてのスタートアップコードを処理するApp.csまたはApp.xaml.csのすぐ隣にあるStartup.csを作成する必要があると考えます。

新規ユーザーは、このブートストラップに関するすべてのことを理解したくないでしょう。 私の意見では、これは特にあなたが望む最後のことです。なぜなら、MAUIはAppDelegate、MainActivityなどの周りのすべての定型文を軽減するからです。1startup/ app.xamlはそれらすべてを支配します。

@aritchieは同意しました

できるだけ多くの人に利用してもらいたいのですが、新しいユーザーの場合、ほとんどの場合、非表示のままにしておくことができます。
しかし、快適になったら、物事を拡張し始めることができます。

ここには、ユーザーがすべてを購入しなくてもここで利点を得ることができるシナリオがあると思います。

たとえば、Shellを使用すると、次のようなRegisterRoute構文を導入できます。

Shell.RegisterRoute<TBindingContext, TPage>(String routeName);
あるいは単に
Shell.RegisterRoute<TType>();

内部では、これらすべてを使用してタイプを作成します。 次に、ユーザーが注入されるHttpContextなどが必要な場合。

この提案の他の部分は、IServiceProviderを介してすべてのDataTemplatesを作成しようとすることです。これにより、いくつかの楽しいシナリオが可能になります。

ジェームズのサンプルに基づいて、ここでシェルを使っていくつかのバリエーションで遊んでいました

https://github.com/PureWeen/AllExtensions-DI-IoC/tree/shell_ioc

@charlesroddie

人々がリフレクション/コンベンション/アノテーションベースの方法でビューモデルを取得したい場合、MAUIは本当に明示的なサポートを提供する必要がありますか?

現在のところ、パフォーマンスを考慮して、リフレクションを通じて何も提供する予定はありません。 パフォーマンスに影響を与えることなく登録を簡素化できる領域があるかもしれませんが、それでもこれらのいくつかを調査する必要があります。

元のコメントも登録構文で更新しました

@PureWeen

Prismのようなものがモデルにプラグインできるように、IServiceCollectionから何らかのルート拡張を行うことをお勧めします。 私はXFを何年も使用していますが、Shellは使用していません。

NS。 services.UseRoute(オプション名)

別の注意点として、Shinyは、他のアイデアが必要な場合に、MicrosoftDIメカニズムの周りで広範囲に使用されています。 リンカーとの恐ろしい戦いにつながるものの量が多かったため、ホストビルダーモデル(現在の形式)を拒否することになりました。 いつか電話でこれらの経験を共有できれば幸いです。

おそらく、ソースジェネレータを使用してパフォーマンスの問題を軽減できますか? その後、カスタム属性を引き続き使用できますが、生成されたソースファイルですべての(デフォルトの)登録を行います。

@aritchieはいいですね!

ビルドの楽しみの反対側になったら、チャットしましょう!

@rogihee

おそらくソースジェネレータ

うん! マウイ以前の現在のレンダラー登録を再構築したいと考えており、その作業のためにソースジェネレーターを検討しています。 私たちがそのルートに行くことになった場合、私たちは間違いなくこれを活用することができます

XamarinフォームにDIがあると便利ですが、DIスコープについては何も読んでいません。 DIスコープの使用は、コンポーネントの寿命を管理するための優れた方法です。 これは、EFコアのデータベースコンテキストを操作するときに特に役立ちます。

スコープを作成し、このスコープでコマンドハンドラオブジェクトを要求するDI対応のICommand実装があると便利です。

WPFアプリケーションで依存性注入を使用することも難しいため、WPFチームと協力して、同様の方法でWPFでDIを使用できるようにすることもできます。

唯一の問題は、デフォルトの実装にすでに実装されているコンテナを使用する必要があるのか​​、それともパフォーマンスに関してもう少しモバイル志向の独自のコンテナをロールする必要があるのか​​ということです。

マウイ島のコンテナをゼロから実装したいと思います。 あなたが見ることができるように、ここでDependecyServiceで実装Xamarin.Forms速いものです。 (私は知っています...その記事は少し古いですが、値があまり変化しないと思います)。
また、パフォーマンスの観点から、独自のDIコンテナを作成する方がはるかに優れています。 しかし、私の懸念は.NET 5のない世界です。.NET5とソースコードジェネレーターを使用すると、この問題に対する別の解決策があるかもしれません。
また、モバイルの場合、重いDIが必要な場合は、何か問題があります。

私はこのリポジトリに来て、MAUIアプリケーションが汎用ホスト(Microsoft.Extensions.Hosting)をサポートするように提案しました。これは、UIアプリケーションにDIを含めるのが非常に自然なため、デフォルトで推奨されます(ただし、保護したい人もいます)。 asp.netコアの依存性注入に慣れている場合は、MAUIでも機能するよりも、理解する必要が少ないことを意味します。 初心者の場合は、構成を最小限に抑えることをお勧めします。

最初である代わりに、私はこの問題を見つけました、そしてそれは良い議論だと思います!

いくつかのサービスと小さなUI(ステータス、構成など)を提供するアプリケーションを持つことは今でも一般的なことです。 何らかのサービスを提供する小さなアプリケーションを構築するたびに、UIを使用してこれを拡張する必要性を見つけ続けます。

このプロジェクトで見られるように、私はすでにWindowsフォームとWPFアプリケーションを汎用ホストで実行するための何かを構築しています: https
これをGreenshotに使用したいと思っていましたが、現在はまだ何もリリースしていません。
サンプルには、ReactiveUI、MahApps、Caliburn.Microを使用するWPFプロジェクトがあります。

ビューを手動でDIに登録させないでください。 ブレイザーを最大限に活用してください、その素晴らしいです! Blazorでは、パラメーターを簡単に渡したり、コンポーネントにサービスを挿入したりできます。 コンポーネント自体を登録する必要はありません。 サービスを登録するだけです。 MVVMはページに最適ですが、これらのセルフホストコンポーネントビューを作成することでこれほど生産性を感じたことはありません。 箱から出してすぐに使えるものがないため、新しいXamarin、WPF、UWPプロジェクトを開始するのは嫌いです。 もう一度、Blazorを見てください。これは、将来のすべてのGUIフレームワークの真の青写真です。

なぜ誰もがこれらの2つの連続したステートメントを作成するのか理解できません。

    services.AddTransient<IMainViewModel, MainViewModel>();
    services.AddTransient<MainPage>();

ビューコードビハインドにサービスを注入することの何が問題になっていますか? 99.99%の確率で必要なものです。

また、コンストラクターインジェクションではなく、ブレイザーでのプロパティインジェクションが非常に簡単であるという理由で気に入っています。

Xamarin.Formshttps ://github.com/hostly-org/hostly用にこれを行うライブラリを実際にリリースしました。 IHostのカスタム実装を使用しており、ネイティブエントリポイントで簡単に設定できます。 たとえば、 MainActivity次のように置き換えます。

LoadApplication(new App());`

と:

new XamarinHostBuilder().UseApplication<App>()
   .UseStartup<Startup>()
   .UsePlatform(this)
   .Start();

また、ミドルウェアをナビゲーションに登録するためのサポートなど、いくつかの追加機能が組み込まれています。

コードをMAUIプロジェクトと非MAUIプロジェクトの両方で共有できるように、System.Maui名前空間の外部にあるDIシステムを使用することに賛成です。

私が確信が持てないのは、Microsoft.Extensions.DependencyInjectionまたはそれに基づくものをDIシステムとして使用することです。 私はこれについて専門家のふりをするつもりはありません–私は確かに複数の最新のDIシステムを自分で使用したことはありません。 しかし、他の人が「依存性注入」の第2版を読んだのではないかと思います。 スティーブン・ヴァン・ダーセンとマーク・シーマンによる「原則、実践、パターン」。 彼らは、第2版のセクションで、Autofac、Simple Injector、およびMicrosoft.Extensions.DependencyInjectionについて説明し、賛否両論と、独自の意見/結論を提供します。 MAUIの世界でMicrosoft.Extensions.DependencyInjection(主に他のMicrosoftシナリオで使用されている)を使用することのいくつかの利点を見ることができますが、複数のDIシステムの経験を持つ誰かが著者の結論とどの程度コメントできるかどうか疑問に思いますそれらは、モバイルとデスクトップの使用に関するMAUIの世界に関連していますか?

このページは役に立ちましたか?
0 / 5 - 0 評価