c# - 将 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 发表评论后,我又查看了链接并注意到了几件事:
- 文档链接的日期大约在同一时间
Microsoft.Extensions.DependencyInjection
仍处于测试阶段 - 据我所知,文档中“使用依赖注入容器的几个优点”列表中的所有点也适用
DependencyInjection
。 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;
}
解决方案
此实现使用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>
推荐阅读
- javascript - 如何更改日期选择器内的日期选择器设置?
- javascript - 对 Promise 的关闭不能与异步影响的值一起使用吗?
- java - CNE 收到意外操作:android.intent.action.BATTERY_CHANGED
- c++ - 如何创建将检查我的校验和的测试(如何使我的代码损坏内存)
- jquery - 如何使用 jquery 访问不同域中 iframe 内的 div 元素
- vim - 如何解决 jupyterlab-vim 的安装错误
- c - 我们可以在头文件中使用 extern 关键字和 typedef 数据类型吗?
- terraform - 无法创建嵌套动态块
- mysql - 如何通过更改列值创建与表中当前行对应的新行
- python - Python 不更新 SQL Server 表