如何解析在 ViewModel 内的 builder.Services 中注册的服务?

Too*_*eve 5 maui

摘要:我希望使用 MAUIbuilder.Services来解析 ViewModel 中的服务,但我不明白该怎么做。

我可以创建自己的IServiceProvider,但我希望避免所需的样板代码,因此我寻求“标准 MAUI”解决方案。

我添加了以下行MauiProgram.CreateMauiApp()

builder.Services.AddSingleton<IAlertService, AlertService>();
Run Code Online (Sandbox Code Playgroud)

以及相应的声明(在其他文件中):

public interface IAlertService
{
    // ----- async calls (use with "await") -----
    Task ShowAlertAsync(string title, string message, string cancel = "OK");
    Task<bool> ShowConfirmationAsync(string title, string message, string accept = "Yes", string cancel = "No");
}

internal class AlertService : IAlertService
{
    // ----- async calls (use with "await") -----

    public Task ShowAlertAsync(string title, string message, string cancel = "OK")
    {
        return Application.Current.MainPage.DisplayAlert(title, message, cancel);
    }

    public Task<bool> ShowConfirmationAsync(string title, string message, string accept = "Yes", string cancel = "No")
    {
        return Application.Current.MainPage.DisplayAlert(title, message, accept, cancel);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在我的 BaseViewModel 类中:

internal class BaseViewModel
{
    protected static IAlertService AlertSvc = ??? GetService (aka Resolve) ???
    
    public static void Test1()
    {
        Task.Run(async () =>
            await AlertSvc.ShowAlertAsync("Some Title", "Some Message"));
    }
}
Run Code Online (Sandbox Code Playgroud)

问:AlertSvcMauiProgram中注册的服务如何填写?

信用:基于一些 SO 讨论或 MAUI 问题中建议的“DialogService”。抱歉,我已经找不到原著了。

Ger*_*uis 17

IServiceProvider所有依赖项都将通过.NET MAUI 的实现提供。幸运的是,它IServiceProvider本身也被添加到依赖注入容器中,因此您可以执行以下操作。

将您需要的内容添加到以下位置的依赖项注入容器中MauiProgram.cs

builder.Services.AddSingleton<IStringService, StringService>();
builder.Services.AddTransient<FooPage>();
Run Code Online (Sandbox Code Playgroud)

为了完整起见,我的StringService样子是这样的:

public interface IStringService
{
    string GetString();
}

public class StringService : IStringService
{
    public string GetString()
    {
        return "string";
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在你的FooPage,当然也可以是你的视图模型或任何东西中,添加一个像这样的构造函数:

public FooPage(IServiceProvider provider)
{
    InitializeComponent();

    var str = provider.GetService<IStringService>().GetString();
}
Run Code Online (Sandbox Code Playgroud)

在这里您可以看到您通过手动解决了该服务,IServiceProvider并且您可以调用该服务。请注意,您需要在此处添加一些错误处理,以查看该服务是否确实可以解决。


Jul*_*ian 9

如果您不能(或不想)使用 MAUI Shell 的自动构造函数注入,您仍然可以利用IServiceProvider属于类的实例MauiApp

您可以只创建一个静态类,而不是IServiceProvider在类中存储对 的实现的引用App(这会在为 ViewModel 等编写单元测试时引入其他依赖性问题) :ServiceHelper

public static class ServiceHelper
{
    public static IServiceProvider Services { get; private set; }

    public static void Initialize(IServiceProvider serviceProvider) => 
        Services = serviceProvider;

    public static T GetService<T>() => Services.GetService<T>();
}
Run Code Online (Sandbox Code Playgroud)

然后,在调用该方法ServiceHelperMauiProgram.cs 文件中对其进行初始化:Build()

public static MauiApp CreateMauiApp()
{
    var builder = MauiApp.CreateBuilder();

    // ...

    builder.Services.AddSingleton<IAlertService, AlertService>();

    var app = builder.Build();

    //we must initialize our service helper before using it
    ServiceHelper.Initialize(app.Services);

    return app;
}
Run Code Online (Sandbox Code Playgroud)

最后,您可以解决类中的任何依赖关系,例如 ViewModel,如下所示:

private IAlertService _alertService;

public class MyViewModel()
{
    _alertService = ServiceHelper.GetService<IAlertService>();
}
Run Code Online (Sandbox Code Playgroud)

这也可以在单元测试中使用,您可以简单地向 提供一个假的或模拟的实现ServiceHelper,例如:

using Moq;

namespace MyTests;

[TestFixture]
public class MyViewModelTests
{
    [Setup]
    public void Setup()
    {
        var alertServiceMock = new Mock<IAlertService>();
        var serviceProviderMock = new Mock<IServiceProvider>();
        serviceProviderMock
            .Setup(sp => sp.GetService<IAlertService>())
            .Returns(alertServiceMock.Object);

        ServiceHelper.Initialize(serviceProviderMock.Object);
    }

    // write tests here...
}
Run Code Online (Sandbox Code Playgroud)

我刚刚还发表了一篇关于该主题的博客文章,其中有更多详细信息。


Too*_*eve 6

可以通过构造函数的参数在不使用 DI 注入的情况下访问服务提供者(如其他答案中所示):

\n
myvar = Application.Current.Handler.MauiContext.Services.GetService<SomeType>();\n
Run Code Online (Sandbox Code Playgroud)\n

我\xe2\x80\x99m 不知道有什么理由这样做而不是使用 DI,但为了完整性我提到它。查看对 MauiContext 的访问也能提供丰富的信息。

\n