Maui: [Spec] Microsoft.Extensions.Hosting et/ou Microsoft.Extensions.DependencyInjection

Créé le 18 mai 2020  ·  21Commentaires  ·  Source: dotnet/maui

Intégrez les fonctionnalités de Microsoft.Extensions.Hosting dans .NET MAUI

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

Utilisez la structure d'hôte générique configurée avec .netcore 3.0 pour initialiser les applications .NET MAUI.

Cela fournira aux utilisateurs une expérience très Microsoft et apportera une grande partie de notre code en ligne avec le noyau ASP.NET

Racine profondément IServiceProvider dans .NET MAUI

Remplacez toutes les instances de Activator.CreateInstance(Type) par IServiceProvider.Get()

Par exemple, si nous modifions cela sur ElementTemplate
https://github.com/dotnet/maui/blob/1a380f3c1ddd9ba76d1146bb9f806a6ed150d486/Xamarin.Forms.Core/ElementTemplate.cs#L26

Ensuite, tout DataTemplate spécifié via type tirera parti d'être créé via l'injection de constructeur.

Exemples

```C#
Host.CreateDefaultBuilder()
.ConfigureHostConfiguration(c =>
{
c.AddCommandLine(new string[] { $"ContentRoot={FileSystem.AppDataDirectory}" });
c.AddJsonFile(fullConfig);
})
.ConfigureServices((c, x) =>
{
nativeConfigureServices(c, x);
Configurer les services (c, x);
})
.ConfigureLogging(l => l.AjouterConsole(o =>
{
o.DisableColors = true;
}))
.Construire();

static void ConfigureServices (HostBuilderContext ctx, services IServiceCollection)
{
si (ctx.HostingEnvironment.IsDevelopment())
{
var monde = ctx.Configuration["Bonjour"];
}

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));
}

Si tous les DataTemplates sont câblés via IServiceProvider, les utilisateurs peuvent spécifier des interfaces sur les DataTemplates

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

Cuit en injection constructeur

```C#
App de classe publique
{
application publique()
{
InitializeComponent();
MainPage = ServiceProvider.GetService();
}
}
classe partielle publique MainPage : ContentPage
{
publique MainPage(IMainViewModel viewModel)
{
InitializeComponent();
BindingContext = viewModel;
}
}

classe publique MainViewModel
{
public MainViewModel(ILoggerenregistreur, IHttpClientFactory httpClientFactory)
{
var httpClient = httpClientFactory.CreateClient();
logger.LogCritical("Toujours se connecter!");
Bonjour = "Bonjour d'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

Tous les ContentTemplates spécifiés dans le cadre de Shell seront créés via le IServiceProvider

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

Détails de mise en œuvre à considérer

Utilisez Microsoft.Build pour faciliter le pipeline de démarrage

Tirez dans les fonctionnalités de l'hôte pour articuler un emplacement de démarrage spécifique où les choses sont enregistrées
https://montemagno.com/add-asp-net-cores-dependency-injection-into-xamarin-apps-with-hostbuilder/

Cela a l'avantage de nous permettre de nous lier aux implémentations de conteneurs IoC qui fonctionnent déjà avec le noyau asp.net

Avantages : Cela donne aux développeurs .NET une expérience cohérente.
Inconvénients : performances ? Est-ce exagéré pour le mobile ?

Options de conteneur DI

Déprécier DependencyService en faveur de Microsoft.Extensions.DependencyInjection

Xamarin.Forms dispose actuellement d'un service de dépendance maison très simple qui ne comporte pas beaucoup de fonctionnalités. Augmenter les fonctionnalités de ce service face aux options déjà disponibles n'a pas beaucoup de sens. Afin de nous aligner de manière plus appropriée sur les autres plates-formes Microsoft, nous pouvons basculer vers le conteneur à l'intérieur de Microsoft.Extensions.DependencyInjection .

L'enregistrement automatique du DependencyService sera lié au nouveau registraire. Si l'utilisateur a choisi que le bureau d'enregistrement effectue une analyse d'assemblage, cela déclenchera le DependencyService pour analyser les attributs de niveau d'assemblage

L'un des inconvénients de l'utilisation de ce conteneur est que les types ne peuvent pas être enregistrés à la volée une fois que l'application a démarré. Vous ne pouvez enregistrer des types que dans le cadre du processus de démarrage, puis une fois l'IServicePRovider construit, c'est tout. L'inscription est fermée pour les affaires

Avantages : C'est un conteneur complet
Inconvénients : performances ?

Convertissez notre DependencyService pour utiliser IServiceCollection en tant que conteneur interne et faites-le implémenter IServiceProvider

Cela nous permettrait d'utiliser un conteneur très mince et sans vedette si les gens veulent simplement les meilleures performances. Nous pourrions probablement l'utiliser par défaut, puis les gens pourraient opter pour le plus complet s'ils le souhaitent.

```C#
classe publique DependencyService : IServiceProvider
{
}

ServiceCollectionExtensions statiques publiques
{
public statique DependencyService Create (ce IServiceCollection);
}
```

Considérations

Est-ce globalement utile pour une nouvelle expérience d'application pour les utilisateurs ? Voulons-nous vraiment ajouter la surcharge de compréhension d'une boucle de démarrage de l'hôte de construction pour les nouveaux utilisateurs ? Il serait probablement utile d'avoir une configuration par défaut pour tout cela qui utilise simplement Init, puis les nouveaux utilisateurs peuvent facilement faire ce dont ils ont besoin sans avoir à configurer les fichiers de paramètres/configurer les services/etc.

Performance

Dans mes tests limités, il faut environ 25 ms pour que les bits d'hébergement Microsoft démarrent. Nous voudrons probablement approfondir ces 25 ms pour voir si nous pouvons le contourner ou si ce coût fait déjà partie d'un coût de démarrage différent que nous encourrons déjà

Rétrocompatibilité

  • Le DependencyService actuel recherche les attributs de niveau d'assemblage que nous allons très probablement passer à l'option .NET MAUI. La valeur par défaut vous obligera à enregistrer des choses via la collection de services explicitement

Difficulté : Moyenne/Grande

Travaux existants :
https://github.com/xamarin/Xamarin.Forms/pull/8220

breaking proposal-open

Commentaire le plus utile

Un élément à prendre en compte lors de l'implémentation consiste à utiliser le projet Microsoft.Extensions.DependencyInjection.Abstractions comme principale dépendance de l'implémentation à Maui. En réduisant l'empreinte de Microsoft.Extensions.DependencyInjection à n'utiliser que lors de la création du conteneur, cela permettra aux bibliothèques et aux frameworks tiers d'être indépendants du conteneur.

Cela permet à un développeur ou à un framework la possibilité d'utiliser un conteneur différent de celui fourni par Maui. En utilisant le projet d'abstractions, le travail requis par le développeur ou le framework consiste simplement à implémenter les interfaces dans le projet d'abstractions. Où tout le code Maui utilisera simplement les interfaces. Cela fournira un point d'extension massif pour tout le monde alors que nous retravaillons le fonctionnement de l'injection de dépendances dans la plate-forme.

Tous les 21 commentaires

Un élément à prendre en compte lors de l'implémentation consiste à utiliser le projet Microsoft.Extensions.DependencyInjection.Abstractions comme principale dépendance de l'implémentation à Maui. En réduisant l'empreinte de Microsoft.Extensions.DependencyInjection à n'utiliser que lors de la création du conteneur, cela permettra aux bibliothèques et aux frameworks tiers d'être indépendants du conteneur.

Cela permet à un développeur ou à un framework la possibilité d'utiliser un conteneur différent de celui fourni par Maui. En utilisant le projet d'abstractions, le travail requis par le développeur ou le framework consiste simplement à implémenter les interfaces dans le projet d'abstractions. Où tout le code Maui utilisera simplement les interfaces. Cela fournira un point d'extension massif pour tout le monde alors que nous retravaillons le fonctionnement de l'injection de dépendances dans la plate-forme.

@ahoefling Oui, c'est ainsi que Maui consommera tout. La seule question est de savoir si nous devons utiliser le conteneur déjà implémenté pour l'implémentation par défaut ou lancer le nôtre qui est un peu plus mobile en ce qui concerne les performances

@PureWeen L'

@PureWeen c'est super à entendre !

Pour autant que je sache, les performances du conteneur du projet d'extensions sont assez solides. Je recommanderais que nous commencions avec le conteneur par défaut et que nous puissions itérer si les tests de performances ne répondent pas à nos attentes. J'ai rassemblé un petit échantillon de code de ce que je pensais pour gérer certains des points d'extension.

Cela nous permet d'échanger facilement le conteneur par les développeurs, les frameworks ou la plate-forme Maui si nous voulons utiliser un conteneur différent.

Peut-être que le System.Maui.Application pourrait ressembler à ceci :
```c#
classe publique Application : 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 pouvons-nous l'utiliser pour permettre aux gens de créer leur propre IServiceProvider ?

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

C'est ce à quoi des choses comme AutoFac s'accrochent

Pour autant que je sache, Host Builder fournit une API riche pour ajouter plus de dépendances par défaut telles que la déconnexion de la boîte. Où mon code suggéré est beaucoup plus maigre. Je ne pense pas que l'une ou l'autre des approches soit plus «correcte» que l'autre, car elles résolvent différents problèmes avec l'injection de dépendance.

Générateur d'hôte
Si nous voulons créer des opinions selon lesquelles la journalisation de la console se produira d'une certaine manière et disons que HttpClientFactory fonctionnera d'une certaine manière, alors Host Builder sera probablement notre meilleure option.

Lean ServiceCollection
Si nous voulons que notre système DI soit aussi léger que possible et que le développeur de l'application décide de ce qui fonctionnera pour lui et de ce qui ne fonctionnera pas, alors je pense que mon code suggéré ou similaire serait le moyen de l'implémenter.

La façon dont je regarde le problème est de savoir quel est le moyen le moins invasif d'échanger le DependencyService . Si l'objectif final est de suivre les modèles en place avec le noyau asp.net et le modèle de constructeur d'hôte, il sera probablement préférable de créer une classe Startup.cs qui utilise le constructeur d'hôte.

Le code dans l'OP est problématique car il n'est pas clair d'après les types impliqués comment ServiceProvider.GetService<MainPage>() fonctionne, et comment MainViewModel est découvert. Cela implique également un null qui devrait déclencher un avertissement NRT. Tout cela semble être un moyen de contourner le système de type. ASP.Net n'est pas un brillant exemple d'API propre.

Il est important que les utilisateurs puissent éviter l'utilisation de cette capacité, comme ils le peuvent maintenant, et spécifier les objets d'une manière sûre pour le type. Et qu'en l'évitant, les utilisateurs évitent également de payer la pénalité de performance.

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

Si les gens veulent obtenir un modèle de vue basé sur la réflexion/convention/annotation, MAUI a-t-il vraiment besoin de fournir un support explicite ? Ne peuvent-ils pas le faire de toute façon ? Le lien original suggère qu'ils peuvent.

@ahoefling

Host Builder vs Lean ServiceCollection

D'accord, déterminer l'avantage de l'un par rapport à l'autre est certainement une grande partie de cela !

Au début, je ne regardais que les collections de services Lean, mais j'ai décidé de me lancer à fond.
Une partie de l'avantage d'utiliser la direction du constructeur est qu'elle est simplement liée aux implémentations netcore existantes. Par exemple, avec AutoFac, vous pouvez simplement utiliser la même syntaxe de boucle de démarrage au lieu d'avoir à inventer notre propre truc. La connaissance du domaine est transférable.

@PureWeen Cela a du sens et c'est une bonne idée de conserver l'unification de .NET 5+ et les compétences transférables entre les différents outils.

Je pense que nous devrions créer un Startup.cs qui se trouve juste à côté du App.cs ou du App.xaml.cs qui gère tout le code de démarrage. Cela unifiera l'histoire de l'injection de dépendance à Maui avec d'autres projets de l'écosystème .NET. Lorsque j'ai implémenté l'injection de dépendances dans les modules DNN l'année dernière, j'ai fait quelque chose de similaire qui a permis aux développeurs de transférer leurs compétences assez facilement.

pensons que nous devrions créer un Startup.cs qui se trouve juste à côté de App.cs ou App.xaml.cs qui gère tout le code de démarrage.

Les nouveaux utilisateurs ne voudront pas comprendre tous ces trucs d'amorçage. À mon avis, c'est la dernière chose que vous voulez, d'autant plus que MAUI allégera tout le passe-partout autour d'AppDelegate, MainActivity, etc. 1 startup/app.xaml pour les gouverner tous.

@aritchie a accepté

J'aimerais permettre aux gens autant que possible, mais pour les nouveaux utilisateurs, la plupart de ces éléments peuvent rester cachés.
Mais une fois qu'ils sont à l'aise, ils peuvent commencer à étendre les choses.

Je pense qu'il existe des scénarios ici où les utilisateurs peuvent obtenir des avantages sans avoir à tout acheter.

Par exemple, avec Shell, nous pourrions simplement introduire une syntaxe RegisterRoute comme

Shell.RegisterRoute<TBindingContext, TPage>(String routeName);
ou juste
Shell.RegisterRoute<TType>();

Cela sous le capot utilise tout cela pour créer les types. Ensuite, si les utilisateurs veulent un HttpContext qui sera injecté, etc.

L'autre partie de cette proposition consiste à essayer de créer également tous les DataTemplates via IServiceProvider, ce qui permet des scénarios amusants.

Je jouais avec quelques variations avec Shell ici basées sur l'échantillon de James

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

@charlesroddie

Si les gens veulent obtenir un modèle de vue basé sur la réflexion/convention/annotation, MAUI a-t-il vraiment besoin de fournir un support explicite ?

Le plan actuel n'est pas de fournir quoi que ce soit par réflexion en raison de considérations de performance. Il peut y avoir des domaines dans lesquels nous pouvons simplifier l'enregistrement sans nuire aux performances, mais nous devons quand même les explorer.

J'ai également mis à jour le commentaire d'origine avec la syntaxe d'enregistrement

@PureWeen

Je recommanderais d'avoir une sorte d'extensions de route à partir de IServiceCollection afin que des choses comme Prism puissent se connecter au modèle. J'utilise XF depuis des années et je n'utilise pas Shell.

c'est à dire. services.UtiliserRoute(nom facultatif)

Sur une autre note, Shiny est largement utilisé autour de la mécanique Microsoft DI si vous avez besoin d'autres idées. J'ai fini par rejeter le modèle hostbuilder (sous sa forme actuelle) en raison de la quantité de choses qu'il a amenées et qui ont conduit à un horrible combat avec l'éditeur de liens. Je serais heureux de partager ces expériences sur un appel à un moment donné.

Peut-être que les générateurs de sources pourraient être utilisés pour atténuer les problèmes de performances ? Ensuite, vous pouvez toujours utiliser les attributs personnalisés mais effectuer tout l'enregistrement (par défaut) dans un fichier source généré.

@aritchie sonne bien !

une fois que nous sommes de l'autre côté de la construction, discutons !

@rogihee

Peut-être des générateurs de sources

Ouais!! Nous voulons restructurer l'enregistrement actuel du moteur de rendu avant Maui et nous examinons les générateurs de source pour ce travail. Si nous finissons par emprunter cette voie, nous pourrions certainement en tirer parti pour ce

Ce serait bien d'avoir DI dans Xamarin Forms, mais je ne lis rien sur les étendues DI. L'utilisation d'étendues DI est un excellent moyen de gérer la durée de vie des composants. C'est particulièrement utile lorsque vous travaillez avec les contextes de base de données du noyau EF.

Ce serait formidable d'avoir une implémentation ICommand consciente

L'utilisation de l'injection de dépendances dans les applications WPF est également difficile, alors faites peut-être équipe avec l'équipe WPF pour permettre l'utilisation de DI dans WPF de la même manière.

La seule question est de savoir si nous devons utiliser le conteneur déjà implémenté pour l'implémentation par défaut ou lancer le nôtre qui est un peu plus mobile en ce qui concerne les performances

Je dirais d'implémenter un conteneur pour le Maui à partir de zéro. Comme vous pouvez le voir ici , le DependecyService implémenté dans le Xamarin.Forms est le plus rapide. (Je sais... Cet article date un peu, mais je ne pense pas que les valeurs changent trop).
Il est également bien mieux - en termes de performances - de créer notre propre conteneur DI. Mais mes préoccupations concernent un monde sans .NET 5. Avec .NET 5 et le générateur de code source, nous pouvons avoir une autre solution à ce problème.
Et, pour le mobile , si vous avez besoin d'une DI lourde, vous faites quelque chose de mal.

Je suis venu sur ce référentiel pour proposer de faire en sorte que les applications MAUI prennent en charge l'hôte générique (Microsoft.Extensions.Hosting), préférable par défaut (bien que certaines personnes puissent vouloir protéger), car il est très naturel d'avoir DI dans une application UI. Cela signifierait que les gens ont moins besoin de comprendre, s'ils sont habitués à l'injection de dépendances pour le noyau asp.net, cela fonctionnera également avec MAUI. Pour les débutants, il peut être bon de garder la configuration très minimaliste.

Au lieu d'être le premier, j'ai trouvé ce problème, et je pense que c'est une bonne discussion !

Avoir des applications qui fournissent certains services et une petite interface utilisateur (statut, configuration, etc.) est toujours une chose courante. Chaque fois que je crée une petite application qui fournit un service, je continue à trouver le besoin de l'étendre avec une interface utilisateur.

Je construis déjà quelque chose pour faire fonctionner les applications Windows Forms et WPF sur l'hôte générique, comme on peut le voir dans ce projet : https://github.com/dapplo/Dapplo.Microsoft.Extensions.Hosting
J'espérais l'utiliser pour Greenshot, mais actuellement je n'ai encore rien sorti avec.
Dans les exemples, il y a des projets WPF qui utilisent ReactiveUI, MahApps et Caliburn.Micro.

S'il vous plaît, ne m'obligez pas à enregistrer les vues manuellement dans DI. Prenez le meilleur de Blazor, c'est génial ! Dans blazor, je peux facilement transmettre des paramètres ou injecter le service dans le composant. Pas besoin d'enregistrer le composant lui-même. J'ai seulement besoin d'enregistrer les services. MVVM est idéal pour les pages, mais je ne me suis jamais senti plus productif en créant ces vues de composants auto-hébergées. Je déteste démarrer un nouveau projet Xamarin, WPF, UWP à cause des choses qui ne sont pas prêtes à l'emploi. Encore une fois, regardez Blazor, c'est un véritable modèle pour tous les futurs frameworks GUI.

Je ne comprends pas pourquoi quelqu'un créerait ces deux déclarations consécutives :

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

Qu'y a-t-il de mal à injecter le service dans le code de vues derrière ? C'est ce que vous voulez 99,99 % du temps.

De plus, j'aime beaucoup l'injection de propriété dans blazor au lieu de l'injection de constructeur, simplement parce que c'est plus facile.

En fait, je viens de publier une bibliothèque qui fait cela pour Xamarin.Forms https://github.com/hostly-org/hostly . Il utilise une implémentation personnalisée d'IHost, qui peut être facilement configurée dans les points d'entrée natifs. par exemple dans MainActivity vous remplaceriez :

LoadApplication(new App());`

avec:

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

il a également quelques goodies supplémentaires intégrés comme la prise en charge de l'enregistrement du middleware avec la navigation.

Je suis favorable à l'utilisation d'un système DI qui se trouve en dehors de l'espace de noms System.Maui afin que le code puisse être partagé avec les projets MAUI et non MAUI.

Là où je suis moins sûr, c'est en ce qui concerne l'utilisation de Microsoft.Extensions.DependencyInjection ou de quelque chose basé sur celui-ci en tant que système DI. Je ne prétendrai pas être un expert en la matière – je n'ai certainement pas moi-même utilisé plusieurs systèmes DI modernes. Cependant, je me demande si d'autres ont lu la deuxième édition de « Dependency Injection. Principes, pratiques et modèles » par Steven van Deursen et Mark Seemann. Ils consacrent une section de la deuxième édition à Autofac, Simple Injector et Microsoft.Extensions.DependencyInjection, fournissant des avantages et des inconvénients, ainsi que leurs propres opinions/conclusions. Bien que je puisse voir certains avantages à utiliser Microsoft.Extensions.DependencyInjection (principalement qu'il est utilisé dans d'autres scénarios Microsoft) dans le monde MAUI, je me demande si quelqu'un ici ayant une expérience de plusieurs systèmes DI pourrait commenter les conclusions des auteurs et dans quelle mesure sont-ils liés au monde MAUI d'utilisation des mobiles et des ordinateurs de bureau ?

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

Yaroslav08 picture Yaroslav08  ·  6Commentaires

PureWeen picture PureWeen  ·  9Commentaires

jsuarezruiz picture jsuarezruiz  ·  6Commentaires

aspnetde picture aspnetde  ·  50Commentaires

adojck picture adojck  ·  15Commentaires