如何在.NET MAUI中使用AutoFac

5
几年前,我创建了一套NuGet包,用于CQRS,并使用AutoFac模块来连接内部组件。我想在我的.NET MAUI开发中使用它们,因此我已将它们更新为.NET 6.0,并与我的MAUI项目链接起来,但我不确定我的注册中缺少什么。我的框架的AutoFac模块注册了一个IDateTimeService,但当我将其添加到已注册类的构造函数中时,它无法解析。

因此,按照AutoFac针对.NET Core的指南,我添加了Populate调用,然后加载了AutoFac模块。

using Autofac;
using Autofac.Extensions.DependencyInjection;
using Pages;
using Perigee.Framework.Services;
using Services;
using ViewModels;


public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });
        
        builder.Services.AddSingleton<IAppNavigationService, AppNavigationService>();
        
        builder.Services.AddSingleton<AppShellViewModel>();
        builder.Services.AddTransient<MainPageViewModel>();

        builder.Services.AddSingleton<AppShell>();
        builder.Services.AddSingleton<MainPage>();
        

        // Hook in AutoFac for the PerigeeFramework services
        var autofacBuilder = new ContainerBuilder();
        autofacBuilder.Populate(builder.Services);

        autofacBuilder.RegisterModule(new ServicesModule());
        autofacBuilder.Build(); // Invokes the Load method on the registered Modules.


        return builder.Build();
    }
}

自动注入模块的启动方式如下:
    public class ServicesModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.Register(c =>
            {
                var config = c.IsRegistered<IDateTimeConfig>() ? c.Resolve<IDateTimeConfig>() : null;
                return new DateTimeService(config);
            }).As<IDateTimeService>().InstancePerLifetimeScope();

这是AppShellViewModel的定义

    public AppShellViewModel(IDateTimeService dateTimeService)

这被注入到AppShell中:

public partial class AppShell : Shell
{
    public AppShell(AppShellViewModel viewModel)
    {
        InitializeComponent();

        BindingContext = viewModel;

在运行时,IDateTimeService 无法解析。我也尝试过不使用模块直接注册到 AutoFac,但它仍然无法解析:

        // Hook in AutoFac for the PerigeeFramework services
        var autofacBuilder = new ContainerBuilder();
        autofacBuilder.Populate(builder.Services);

        autofacBuilder.RegisterType<DateTimeService>().As<IDateTimeService>().SingleInstance();
        
        var cont = autofacBuilder.Build();


        return builder.Build();
    }

enter image description here

我需要使用除了.NET DI之外的东西的主要原因是因为架构利用修饰器,SimpleInjector和AutfoFac提供了开箱即用的修饰器,所以我选择了AutoFac。无论哪种情况,我都需要使用这种“交叉线”方法来使用AutoFac和.NET DI,因为MAUI正在使用内置的DI。有人知道我漏掉了什么步骤,导致AutoFac模块的注册信息未出现在IServiceCollection中吗?或者我可以完全用AutoFac替换MauiApp中的.NET DI吗?
编辑: 我已经组合了一个简化版的应用程序。我想也许我需要将一个新的AutoFacServiceProvider传递到App和ISomeService中,当使用AutoFac注册时,它确实会解析enter image description here
但是,如果我尝试将ISomeService注入到另一个已注册的类中,对MainPage = serviceProvider.GetService()的调用将无法解析。如果服务是使用标准DI注册的,它将会起作用。

有人知道如何将AutoFac服务提供程序传播为Maui将使用的服务提供程序吗? 项目在这里

2个回答

3
MauiAppBuilder(从MauiProgram调用)具有一个名为ConfigureContainer的方法,它需要一个Autofac提供的AutofacServiceProviderFactory作为IServiceProvider工厂,并且可选地可以采取一个Action<ContainerBuilder>委托,在其中定义您的配置。

在您的情况下,可能看起来像这样:

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            })
            .ConfigureContainer(new AutofacServiceProviderFactory(), autofacBuilder => {
                // Registrations
                // Don't call the autofacBuilder.Build() here - it is called behind the scenes
            });

        return builder.Build();
    }
}

1
谢谢分享答案,很有帮助。但是,我不知道如何从项目中的任何地方访问容器实例。请帮我解决这个问题。 - Akshay Kumar
3
@AkshayKumar - 我不知道是否有一种直接访问容器的方法。但是,如果只需要获取任何已注册的组件-您可以在项目的任何构造函数中将IServiceProvider作为依赖项。(它将被解析)使用它,您可以调用GetService<YourComponentType>()来访问任何已注册的组件。 例如:YourComponentConstructor(IServiceProvider provider) { var yourComponent = provider.GetService() } - BetaDeltic

1
看起来你可以通过AutofacServiceProviderFactory来获取Autofac.ContainerBuilder的访问权限。它的构造函数接受一个Action<ContainerBuilder>参数。类似这样的写法:
public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            })
            .ConfigureContainer(
                new AutofacServiceProviderFactory(containerBuilder =>
                {

                }));

        return builder.Build();
    }
}

可能更有意义的是创建自己的IServiceProviderFactory<Autofac.ContainerBuilder>,并将其传递给AutofacServiceProviderFactory。这样,您可以做AutofacServiceProviderFactory所做的事情,但同时也负责创建容器,并且在那时您将直接访问它。类似于以下内容:
public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            })
            .ConfigureContainer(
                new MyServiceProviderFactory());

        return builder.Build();
    }
}
...
public class MyServiceProviderFactory : IServiceProviderFactory<Autofac.ContainerBuilder>
{
    public ContainerBuilder CreateBuilder(IServiceCollection services)
    {
        // this is what AutofacServiceProviderFactory essentially does
        var builder = new ContainerBuilder();

        builder.Populate(services);

        return builder;
    }

    public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder)
    {   // this is what AutofacServiceProviderFactory essentially does
        if (containerBuilder == null)
        {
            throw new ArgumentNullException(nameof(containerBuilder));
        }

        var container = containerBuilder.Build();

        // put your code to use the container here

        return new AutofacServiceProvider(container);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接