首页 > 解决方案 > 将 xamarin 表单与 IServiceProvider 一起使用

问题描述

我正在研究 xamarin 表单上的“依赖注入”,并发现了一些使用类似ContainerBuilder. 网上找到的解决方案,例如,讨论如何设置 DI 并将它们注入到您的视图模型中。然而,就个人而言,我没有发现这个或整个视图模型和绑定的概念非常整洁,原因有几个。我宁愿创建可以被业务逻辑重用的服务,这似乎使代码更干净。我觉得实施 anIServiceProvider会导致更清洁的实施。我正计划实施这样的服务提供者:

IServiceProvider Provider = new ServiceCollection()
                            .AddSingleton<OtherClass>()
                            .AddSingleton<MyClass>()
                            .BuildServiceProvider();

首先,我不确定为什么没有这些 xamarin 示例。所以,我不确定朝着这个方向前进是否有什么问题。我已经看过ServiceCollection课了。它来自的包,Microsoft.Extensions.DependencyInjection其名称中没有“aspnetcore”。但是,它的所有者是“aspnet”。我不完全确定是否ServiceCollection仅适用于 Web 应用程序,或者将其用于移动应用程序是否有意义。

只要我使用所有单例就可以安全使用IServiceProvider吗?ServiceCollection有什么问题(在性能或内存方面)我失踪了吗?

更新

在 Nkosi 发表评论后,我又查看了链接并注意到了几件事:

  1. 文档链接的日期大约在同一时间Microsoft.Extensions.DependencyInjection仍处于测试阶段
  2. 据我所知,文档中“使用依赖注入容器的几个优点”列表中的所有点也适用DependencyInjection
  3. Autofac过程似乎围绕着我试图避免使用的 ViewModel。

更新 2

在导航功能的帮助下,我设法让 DI 直接进入页面的后面代码,如下所示:

public static async Task<TPage> NavigateAsync<TPage>()
    where TPage : Page
{
    var scope = Provider.CreateScope();
    var scopeProvider = scope.ServiceProvider;
    var page = scopeProvider.GetService<TPage>();
    if (navigation != null) await navigation.PushAsync(page);
    return page;
}

标签: c#xamarin.formsdependency-injectionarchitectureservice-provider

解决方案


此实现使用Splat和一些帮助器/包装器类来方便地访问容器。

服务的注册方式有点冗长,但它可以涵盖我迄今为止遇到的所有用例;并且生命周期也可以很容易地更改,例如切换到延迟创建服务。

只需使用ServiceProvider类从代码中任何位置的 IoC 容器中检索任何实例。

注册您的服务

public partial class App : Application
{
    public App()
    {
        InitializeComponent();
        SetupBootstrapper(Locator.CurrentMutable);
        MainPage = new MainPage();
    }

    private void SetupBootstrapper(IMutableDependencyResolver resolver)
    {
        resolver.RegisterConstant(new Service(), typeof(IService));
        resolver.RegisterLazySingleton(() => new LazyService(), typeof(ILazyService));
        resolver.RegisterLazySingleton(() => new LazyServiceWithDI(
            ServiceProvider.Get<IService>()), typeof(ILazyServiceWithDI));
        // and so on ....
    }

服务提供者的使用

// get a new service instance with every call
var brandNewService = ServiceProvider.Get<IService>();

// get a deferred created singleton
var sameOldService = ServiceProvider.Get<ILazyService>();

// get a service which uses DI in its contructor
var another service = ServiceProvider.Get<ILazyServiceWithDI>();

ServiceProvider的实现

public static class ServiceProvider
{
    public static T Get<T>(string contract = null)
    {
        T service = Locator.Current.GetService<T>(contract);
        if (service == null) throw new Exception($"IoC returned null for type '{typeof(T).Name}'.");
        return service;
    }

    public static IEnumerable<T> GetAll<T>(string contract = null)
    {
        bool IsEmpty(IEnumerable<T> collection)
        {
            return collection is null || !collection.Any();
        }

        IEnumerable<T> services = Locator.Current.GetServices<T>(contract).ToList();
        if (IsEmpty(services)) throw new Exception($"IoC returned null or empty collection for type '{typeof(T).Name}'.");

        return services;
    }
}

这是我的 csproj 文件。没什么特别的,我添加的唯一 nuget 包是 Spat

共享项目 csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <ProduceReferenceAssembly>true</ProduceReferenceAssembly>
  </PropertyGroup>

  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <DebugType>portable</DebugType>
    <DebugSymbols>true</DebugSymbols>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Splat" Version="9.3.11" />
    <PackageReference Include="Xamarin.Forms" Version="4.3.0.908675" />
    <PackageReference Include="Xamarin.Essentials" Version="1.3.1" />
  </ItemGroup>
</Project>

推荐阅读