Maui: [Спецификация] Microsoft.Extensions.Hosting и / или Microsoft.Extensions.DependencyInjection

Созданный на 18 мая 2020  ·  21Комментарии  ·  Источник: dotnet/maui

Включите функции Microsoft.Extensions.Hosting в .NET MAUI

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

Используйте структуру Generic Host, настроенную с помощью .netcore 3.0, для инициализации приложений .NET MAUI.

Это предоставит пользователям опыт работы с Microsoft и приведет большую часть нашего кода в соответствие с ядром ASP.NET.

Глубоко укоренить IServiceProvider в .NET MAUI

Замените все экземпляры Activator.CreateInstance (Type) на IServiceProvider.Get ()

Например, если мы изменим это в ElementTemplate
https://github.com/dotnet/maui/blob/1a380f3c1ddd9ba76d1146bb9f806a6ed150d486/Xamarin.Forms.Core/ElementTemplate.cs#L26

Тогда любой DataTemplate, указанный с помощью type, будет создан с помощью внедрения конструктора.

Примеры

`` С #
Host.CreateDefaultBuilder ()
.ConfigureHostConfiguration (c =>
{
c.AddCommandLine (новая строка [] {$ "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)
{
если (ctx.HostingEnvironment.IsDevelopment ())
{
var world = ctx.Configuration ["Привет"];
}

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

Если все шаблоны данных подключены через IServiceProvider, пользователи могут указывать интерфейсы в шаблонах данных.

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

Запеченный в конструкторе инъекции

`` С #
приложение общедоступного класса
{
общедоступное приложение ()
{
InitializeComponent ();
MainPage = ServiceProvider.GetService();
}
}
общедоступный частичный класс MainPage: ContentPage
{
общедоступная MainPage (IMainViewModel viewModel)
{
InitializeComponent ();
BindingContext = viewModel;
}
}

открытый класс MainViewModel
{
общедоступная MainViewModel (ILoggerрегистратор, IHttpClientFactory httpClientFactory)
{
var httpClient = httpClientFactory.CreateClient ();
logger.LogCritical («Всегда вести журнал!»);
Hello = "Привет от 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, указанные как часть Shell, будут созданы через 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/

Это позволяет нам подключаться к реализациям контейнеров IoC, которые уже работают против ядра asp.net.

Плюсы: это дает разработчикам .NET единообразный опыт.
Минусы: Производительность? Это перебор для мобильных устройств?

Варианты контейнера DI

Упразднить DependencyService в пользу Microsoft.Extensions.DependencyInjection

Xamarin.Forms в настоящее время имеет очень простую домашнюю службу зависимостей, которая не имеет большого количества функций. Расширять возможности этого сервиса перед уже доступными опциями не имеет особого смысла. Чтобы лучше согласоваться с другими платформами Microsoft, мы можем переключиться на контейнер внутри Microsoft.Extensions.DependencyInjection .

Автоматическая регистрация DependencyService будет привязана к новому регистратору. Если пользователь выбрал для регистратора сканирование сборки, тогда DependencyService запустит сканирование на предмет атрибутов уровня сборки.

Одно из предостережений при использовании этого контейнера заключается в том, что типы не могут быть зарегистрированы на лету после запуска приложения. Вы можете регистрировать типы только как часть процесса запуска, а затем, когда IServicePRovider будет построен, все. Регистрация закрыта для бизнеса

Плюсы: это полнофункциональный контейнер.
Минусы: Производительность?

Преобразуйте нашу DependencyService, чтобы использовать IServiceCollection в качестве внутреннего контейнера, и пусть он реализует IServiceProvider.

Это позволило бы нам использовать очень упрощенный контейнер без каких-либо функций, если люди просто хотят максимальной производительности. Вероятно, мы могли бы использовать это по умолчанию, а затем люди могли бы выбрать более популярный, если захотят.

`` С #
открытый класс DependencyService: IServiceProvider
{
}

общедоступный статический ServiceCollectionExtensions
{
общедоступный статический DependencyService Create (это IServiceCollection);
}
`` ''

Соображения

Полезно ли это в целом для новых пользователей приложения? Неужели мы действительно хотим добавить накладные расходы на понимание цикла запуска узла сборки для новых пользователей? Вероятно, было бы полезно иметь настройку по умолчанию для всего этого, которая просто использует Init, а затем новые пользователи могут просто делать то, что им нужно, без необходимости настраивать файлы настроек / configureservices / и т. Д.

Представление

В моих ограниченных тестах для запуска битов Microsoft Hosting требуется около 25 мс. Мы, вероятно, захотим глубже погрузиться в эти 25 мс, чтобы увидеть, сможем ли мы обойти это или эти затраты уже являются частью других затрат на запуск, которые мы уже понесем.

Обратная совместимость

  • Текущая служба DependencyService сканирует атрибуты уровня сборки, которые мы, скорее всего, переключим на использование .NET MAUI. По умолчанию вам потребуется явно регистрировать вещи через Service Collection.

Сложность: средняя / большая

Существующие работы:
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 Текущая реализация контейнера по умолчанию хорошо работает на мобильных устройствах. Однако количество зависимостей, которые вносит конструктор хостов, ошеломляет.

@PureWeen, это приятно слышать!

Насколько я понимаю, производительность контейнера проекта расширений довольно высока. Я бы порекомендовал начать с контейнера по умолчанию, и мы можем повторить итерацию, если тесты производительности не соответствуют нашим ожиданиям. Я собрал небольшой образец кода того, что я думал, чтобы обработать некоторые из точек расширения.

Это позволяет нам легко менять контейнер разработчиками, фреймворками или платформой Maui, если мы хотим использовать другой контейнер.

Может быть, System.Maui.Application может выглядеть примерно так:
`` С #
Приложение открытого класса: Element, IResourcesProvider, IApplicationController, IElementConfiguration
{
публичный контейнер IServiceProvider {получить; }

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 можем ли мы использовать это, чтобы позволить людям создавать свои собственные IServiceProvider?

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

Вот к чему подключаются такие вещи, как AutoFac

Насколько я понимаю, Host Builder предоставляет богатый API для добавления дополнительных зависимостей по умолчанию, таких как выход из коробки. Где мой предлагаемый код намного более скудный. Я не думаю, что какой-либо из подходов более «правильный», чем другой, поскольку они решают разные проблемы с помощью внедрения зависимостей.

Host Builder
Если мы хотим создать мнение, что ведение журнала консоли будет происходить определенным образом, и допустим, HttpClientFactory будет работать определенным образом, то Host Builder, вероятно, будет нашим лучшим вариантом.

Бережливое обслуживание
Если мы хотим, чтобы наша система DI была как можно более компактной и позволяла разработчику приложения решать, что будет работать для него, а что нет, то я думаю, что мой предлагаемый код или аналогичный будет способ его реализовать.

Я смотрю на проблему как наименее инвазивный способ заменить DependencyService . Если конечная цель состоит в том, чтобы следовать шаблонам на месте с ядром asp.net и моделью компоновщика хоста, вероятно, лучше всего будет создать класс Startup.cs который использует компоновщик хоста.

Код в OP проблематичен, поскольку из задействованных типов неясно, как работает ServiceProvider.GetService<MainPage>() и как обнаруживается MainViewModel . Он также включает null который должен вызвать предупреждение NRT. Все это похоже на способ обойти систему типов. ASP.Net - не лучший пример чистого API.

Важно, чтобы пользователи могли избегать использования этой возможности, как они могут сейчас, и указывать объекты безопасным для типов способом. И что, избегая этого, пользователи также избегают потери производительности.

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

Если люди хотят получить модель представления на основе отражения / соглашения / аннотации, действительно ли MAUI нужно предоставлять явную поддержку? В любом случае они не могут этого сделать? Исходная ссылка предполагает, что они могут.

@ahoefling

Host Builder против Lean ServiceCollection

Правильно, выяснение преимущества одного по сравнению с другим определенно большая часть этого!

Сначала я просто смотрел на Lean Service Collections, но решил пойти ва-банк.
Отчасти преимущество использования направления Builder заключается в том, что оно просто связано с существующими реализациями netcore. Например, с AutoFac вы можете просто использовать тот же синтаксис цикла запуска, а не изобретать что-то свое. Знания в предметной области можно передавать.

@PureWeen В этом есть смысл, и это хорошая идея, чтобы сохранить унификацию .NET 5+ и возможность передачи навыков между различными инструментами.

Я думаю, мы должны создать Startup.cs который находится рядом с App.cs или App.xaml.cs который обрабатывает весь код запуска. Это объединит историю внедрения зависимостей в Мауи с другими проектами в экосистеме .NET. Когда в прошлом году я реализовал внедрение зависимостей в модули DNN, я сделал нечто подобное, что позволило разработчикам довольно легко передать свои навыки.

думаю, нам следует создать Startup.cs, который находится рядом с App.cs или App.xaml.cs, который обрабатывает весь код запуска.

Новые пользователи не захотят разбираться во всех этих вещах, связанных с начальной загрузкой. На мой взгляд, это последнее, что вам нужно, тем более что MAUI избавит от всех шаблонов вокруг AppDelegate, MainActivity и т. Д. 1 startup / app.xaml, чтобы управлять ими всеми.

@aritchie согласился

Я хотел бы предоставить людям как можно больше возможностей, но для новых пользователей большая часть этого может оставаться скрытой.
Но затем, когда они почувствуют себя комфортно, они могут начать расширять работу.

Я думаю, здесь есть сценарии, в которых пользователи могут получить преимущества, не покупаясь на все.

Например, с помощью Shell мы могли бы просто ввести синтаксис RegisterRoute, например

Shell.RegisterRoute<TBindingContext, TPage>(String routeName);
или просто
Shell.RegisterRoute<TType>();

Это под капотом использует все это для создания типов. Затем, если пользователям нужен HttpContext, который будет вставлен в и т. Д.

Другая часть этого предложения - попытаться создать все шаблоны DataTemplates через IServiceProvider, что позволяет реализовать несколько забавных сценариев.

Здесь я играл с некоторыми вариациями с Shell на основе образца Джеймса

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

@charlesroddie

Если люди хотят получить модель представления на основе отражения / соглашения / аннотации, действительно ли MAUI нужно предоставлять явную поддержку?

В настоящее время план не предусматривает каких-либо отражений из соображений производительности. Могут быть некоторые области, в которых мы можем упростить регистрацию без снижения производительности, но некоторые из них все же необходимо изучить.

Я также обновил исходный комментарий синтаксисом регистрации

@PureWeen

Я бы порекомендовал иметь какие-то расширения маршрута вне IServiceCollection, чтобы такие вещи, как Prism, могли подключаться к модели. Я использую XF много лет и не использую Shell.

т.е. services.UseRoute(необязательное имя)

С другой стороны, Shiny широко использует механику Microsoft DI, если вам нужны другие идеи. В итоге я отверг модель hostbuilder (в ее нынешнем виде) из-за того, что она принесла много вещей, которые привели к ужасной борьбе с компоновщиком. Когда-нибудь я был бы счастлив поделиться этим опытом во время телеконференции.

Возможно, можно было бы использовать генераторы исходного кода для устранения проблем с производительностью? Затем вы по-прежнему можете использовать настраиваемые атрибуты, но выполнять всю (по умолчанию) регистрацию в сгенерированном исходном файле.

@aritchie звучит хорошо!

как только мы перейдем на другую сторону веселой сборки, давайте поболтаем!

@rogihee

Возможно Генераторы Источников

Да!! Мы хотим реструктурировать текущую регистрацию рендерера до Мауи, и мы ищем генераторы исходного кода для этой работы. Если мы в конечном итоге пойдем по этому пути, мы определенно сможем использовать это для этого

Было бы здорово иметь DI в Xamarin Forms, но я ничего не читал об областях DI. Использование осциллографов DI - отличный способ управлять сроком службы компонентов. Это особенно полезно при работе с контекстами базы данных ядра EF.

Было бы здорово иметь реализацию ICommand с учетом DI, которая создает область и запрашивает объект обработчика команд в этой области.

Использование внедрения зависимостей в приложениях WPF также сложно, поэтому, возможно, объединитесь с командой WPF, чтобы разрешить использование DI в WPF аналогичным образом.

Вопрос только в том, нужно ли нам использовать уже реализованный контейнер для реализации по умолчанию или использовать собственный, который немного более мобильный с точки зрения производительности.

Я бы сказал реализовать контейнер для Мауи с нуля. Как вы можете видеть здесь , DependecyService реализованный в Xamarin.Forms является более быстрым. (Я знаю ... Эта статья немного устарела, но я не думаю, что ценности меняются слишком сильно).
Также лучше - с точки зрения производительности - создать собственный контейнер DI. Но меня беспокоит мир без .NET 5. С .NET 5 и генератором исходного кода у нас может быть другое решение этой проблемы.
А для мобильных устройств , если вам нужен тяжелый DI, вы делаете что-то не так.

Я пришел в этот репозиторий, чтобы предложить сделать так, чтобы приложения MAUI поддерживали общий хост (Microsoft.Extensions.Hosting), что предпочтительно по умолчанию (хотя некоторые люди могут захотеть защитить), поскольку очень естественно иметь DI в приложении пользовательского интерфейса. Это будет означать, что людям нужно меньше понимать, если они привыкли к внедрению зависимостей для ядра asp.net, чем это также будет работать с MAUI. Для новичков было бы неплохо сохранить минималистичную конфигурацию.

Вместо того, чтобы быть первым, я нашел эту проблему и считаю ее хорошим обсуждением!

Наличие приложений, которые предоставляют некоторые услуги и небольшой пользовательский интерфейс (статус, конфигурация и т. Д.), По-прежнему является обычным явлением. Каждый раз, когда я создаю небольшое приложение, которое предоставляет какую-либо услугу, я все время обнаруживаю необходимость расширить его с помощью пользовательского интерфейса.

Я уже создал что-то, чтобы приложения Windows Forms и WPF работали на общем хосте, как видно из этого проекта: https://github.com/dapplo/Dapplo.Microsoft.Extensions.Hosting
Я надеялся использовать это для Greenshot, но в настоящее время я еще ничего с ним не выпустил.
В примерах представлены проекты WPF, использующие ReactiveUI, MahApps и Caliburn.Micro.

Пожалуйста, не заставляйте меня вручную регистрировать просмотры в DI. Возьмите лучшее от Blazor, это здорово! В Blazor я могу легко передать параметры или внедрить службу в компонент. Самостоятельно регистрировать компонент не нужно. Мне нужно только зарегистрировать услуги. MVVM отлично подходит для страниц, но я никогда не чувствовал себя более продуктивным, создавая эти самодостаточные представления компонентов. Я ненавижу запускать новый проект Xamarin, WPF, UWP из-за вещей, которых нет в стандартной комплектации. Еще раз взгляните на Blazor, это настоящий план для всех будущих GUI-фреймворков.

Я не могу понять, зачем кому-то понадобилось создавать эти два следующих друг за другом утверждения:

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

Что не так с внедрением сервиса в код представлений? Это то, что вы хотите в 99,99% случаев.

Также мне очень нравится инъекция свойств в blazor вместо инъекции конструктора, просто потому, что это проще.

На самом деле я только что выпустил библиотеку, которая делает это для Xamarin.Forms https://github.com/hostly-org/hostly . Он использует настраиваемую реализацию IHost, которую можно легко настроить в собственных точках входа. например, в MainActivity вы должны заменить:

LoadApplication(new App());`

с участием:

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

он также имеет некоторые дополнительные встроенные возможности, такие как поддержка регистрации промежуточного программного обеспечения с помощью навигации.

Я выступаю за использование системы DI, которая находится за пределами пространства имен System.Maui, чтобы код можно было совместно использовать как с проектами MAUI, так и с проектами, не относящимися к MAUI.

Где я менее уверен, так это в отношении использования Microsoft.Extensions.DependencyInjection или чего-то еще, основанного на нем, в качестве этой системы DI. Я не буду претендовать на то, чтобы быть экспертом в этом вопросе - я, конечно же, сам не использовал несколько современных систем DI. Однако мне интересно, читали ли другие второе издание «Внедрения зависимостей. Принципы, практики и шаблоны »Стивена ван Дерсена и Марка Симанна. Они посвятили раздел во втором издании обзору Autofac, Simple Injector и Microsoft.Extensions.DependencyInjection, предоставив за и против, а также свои собственные мнения / выводы. Хотя я вижу некоторые преимущества использования Microsoft.Extensions.DependencyInjection (в первую очередь то, что он используется в других сценариях Microsoft) в мире MAUI, мне интересно, может ли кто-нибудь здесь, имеющий опыт работы с несколькими системами DI, прокомментировать выводы авторов и в какой степени они относятся к миру мобильных и настольных компьютеров MAUI?

Была ли эта страница полезной?
0 / 5 - 0 рейтинги

Смежные вопросы

aspnetde picture aspnetde  ·  50Комментарии

adojck picture adojck  ·  15Комментарии

4creators picture 4creators  ·  31Комментарии

jsuarezruiz picture jsuarezruiz  ·  3Комментарии

probonopd picture probonopd  ·  50Комментарии