Заменить контейнер IoC по умолчанию в MvvmCross

#xamarin #.net-core #inversion-of-control #mvvmcross

#xamarin #.net-ядро #инверсия управления #mvvmcross

Вопрос:

Поскольку MvvmCross v7 использует свой собственный контейнер IoC, я хотел бы заменить его на .NET Core, чтобы упростить жизнь при регистрации сторонних библиотек, таких как IHttpClientFactory , Polly Automapper , и т.д., с помощью уже встроенных методов расширений.

Для достижения этой цели я успешно создал класс, реализация MvxSingleton<IMvxIoCProvider> которого описана следующим образом:

 public class HostingAdapter : MvxSingleton<IMvxIoCProvider>, IMvxIoCProvider
{
    private IServiceProvider ServiceProvider;
    private IServiceCollection ServiceCollection;

    public HostingAdapter()
    {
        var host = Host               
            .ConfigureServices((context, serviceCollection) =>
            {
                // Configure local services
                ConfigureServices(context, serviceCollection);

                ServiceCollection = serviceCollection;
                ServiceProvider = ServiceCollection.BuildServiceProvider();
            })
            .Build();
    }

    public void RegisterType<TFrom, TTo>() where TFrom : class where TTo : class, TFrom
    {
         ServiceCollection.AddTransient<TFrom, TTo>();
         ServiceProvider = ServiceCollection.BuildServiceProvider();
    }

    public T GetSingleton<T>() where T : class
    {
        return ServiceProvider.GetRequiredService<T>();
    }

    public object GetSingleton(Type type)
    {
        return ServiceProvider.GetRequiredService(type);
    }
  

.. и все необходимые методы, запрошенные интерфейсом.
Затем на стороне, зависящей от платформы, я переопределяю создание IoC следующим образом:

 protected override IMvxIoCProvider CreateIocProvider()
{
    var hostingAdapter = new HostingAdapter();
    return hostingAdapter;
}
  

Кажется, что код работает, но как только приложение запускается, Mvx регистрирует свои собственные «дополнительные» сервисы, такие как IMvxLoggerProvider , IMvxSettings и так далее. И здесь возникают проблемы:

  1. ServiceProvider = ServiceCollection.BuildServiceProvider(); вызывается во время Host инициализации, но Mvx все равно продолжает регистрировать службы после этого. Это означает, что IServiceProvider не синхронизирован с IServiceCollection и требуется новый ServiceCollection.BuildServiceProvider(); вызов. Я временно решил обновить поставщика при каждой регистрации коллекции (как в приведенном выше коде), но я знаю, что это влияет на производительность. Кто-нибудь знает, как это обойти?

  2. Существует множество служб Mvx, которые не зарегистрированы, поэтому приложение не запускается. Это IMvxLogProvider , IMvxAndroidLifetimeMonitor , IIMvxSettings , IMvxStart , и т.д. Мне просто интересно, почему? Как позволить Mvx обрабатывать регистрацию в моем контейнере всего, что ему нужно для запуска? Я частично решил некоторые из них, такие как проблема с регистратором, заменяющая значение по умолчанию пользовательским, но другие обратные вызовы, такие как InitializeLifetimeMonitor , вызываются слишком поздно для регистрации.

  3. Нужно ли мне что-либо менять в моей MvxApplication , чем в самой стандартной реализации?

  4. Действительно ли я вынужден заменять стандартный контейнер IoC? Как я могу обрабатывать методы IServiceCollection расширения, которые предоставляют сторонние библиотеки services.AddHttpClient(); ?

Если это необходимо, я использую Xamarin classic, используя платформу Droid. Спасибо

Комментарии:

1. Вопрос в его текущем состоянии является неполным и, следовательно, неясным. Откуда взялся Host пример?

2. Я думаю, для этого потребуется переписать MvxSetup, чтобы не вызывать BuildServiceProvider() для каждого вызова. Добавление поддержки или замена IoC в MvvmCross на Microsoft DI — это то, о чем я думал, просто у меня еще не было времени поиграть.

3. @Nkosi Host — это статический класс .NET Core. Почему неясно? Вы можете понять почти все из названия, остальное — это то, как я подошел к проблеме. @Cheesebaron Это то, что я могу сделать на стороне клиента, или мне дождаться поддержки Mvx? Кстати, я хотел бы открыть PR в официальном репозитории Mvx, но, к сожалению, я не могу :/

Ответ №1:

Намеренно вдохновленный Unity.Майкрософт.Репозиторий DependencyInjection Я обошел эту проблему, подойдя к проблеме наоборот: вместо замены контейнера IoC по умолчанию я вручную инициализирую IServiceCollection экземпляр и добавляю его к поставщику IoC Mvx.

Для достижения этой цели я использовал следующий код:

 public class App : MvxApplication
    {
        public override void Initialize()
        {
            base.Initialize();

            InitializeServiceCollection();

            CreatableTypes()
                .EndingWith("Service")
                .AsInterfaces()
                .RegisterAsLazySingleton();

            RegisterAppStart<HomeViewModel>();
        }

        private static void InitializeServiceCollection()
        {
            IServiceCollection serviceCollection = new ServiceCollection();
            ConfigureServices(serviceCollection);

            IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();

            MapServiceCollectionToMvx(serviceProvider, serviceCollection);
        }

        private static void ConfigureServices(IServiceCollection serviceCollection)
        {
            serviceCollection.AddHttpClient();
        }

        private static void MapServiceCollectionToMvx(IServiceProvider serviceProvider,
            IServiceCollection serviceCollection)
        {
            foreach (var serviceDescriptor in serviceCollection)
            {
                if (serviceDescriptor.ImplementationType != null)
                {
                    Mvx.IoCProvider.RegisterType(serviceDescriptor.ServiceType, serviceDescriptor.ImplementationType);
                }
                else if (serviceDescriptor.ImplementationFactory != null)
                {
                    var instance = serviceDescriptor.ImplementationFactory(serviceProvider);
                    Mvx.IoCProvider.RegisterSingleton(serviceDescriptor.ServiceType, instance);
                }
                else if (serviceDescriptor.ImplementationInstance != null)
                {
                    Mvx.IoCProvider.RegisterSingleton(serviceDescriptor.ServiceType, serviceDescriptor.ImplementationInstance);
                }
                else
                {
                    throw new InvalidOperationException("Unsupported registration type");
                }
            }
        }
    }
  

Комментарии:

1. Возможно, вы захотите изменить var instance = serviceDescriptor.ImplementationFactory(serviceProvider); Mvx.IoCProvider.RegisterSingleton(serviceDescriptor.ServiceType, instance); это MvvmCross.Mvx.IoCProvider.RegisterSingleton(serviceDescriptor.ServiceType,()=> serviceDescriptor.ImplementationFactory(serviceProvider)); , чтобы избежать быстрой загрузки. Также перед регистрацией в IocProvider вам необходимо проверить LifeTime и зарегистрировать соответствующим образом