如何在 Avalonia.ReactiveUI 中使用 Autofac 作为 DI 容器?

JAD*_*JAD 4 c# autofac reactiveui avaloniaui

我正在尝试使用 Autofac 覆盖 Avalonia 和 ReactiveUI 中使用的 DI 容器。到目前为止,我已尝试按照存储库中的说明Splat.Autofac进行操作,但我无法让 Avalonia 工作。

作为一个工作示例,我从 ReactiveUI.Samples存储库中获取了 HelloWorld 示例。

我能想到两个地方可以覆盖 Splat。在App.xaml.cs方法中OnFrameworkInitializationCompleted

using System.Reflection;

using Autofac;

using Avalonia;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
using Avalonia.Threading;

using ReactiveUI;

using Splat;
using Splat.Autofac;

namespace ReactiveAvalonia.HelloWorld {
    public class App : Application {
        public override void Initialize() {
            AvaloniaXamlLoader.Load(this);
        }

        public override void OnFrameworkInitializationCompleted()
        {
            // Build a new Autofac container.
            var builder = new ContainerBuilder();
            builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces();

            // Use Autofac for ReactiveUI dependency resolution.
            // After we call the method below, Locator.Current and
            // Locator.CurrentMutable start using Autofac locator.
            AutofacDependencyResolver resolver = new AutofacDependencyResolver(builder);
            Locator.SetLocator(resolver);

            // These .InitializeX() methods will add ReactiveUI platform 
            // registrations to your container. They MUST be present if
            // you *override* the default Locator.
            Locator.CurrentMutable.InitializeSplat();
            Locator.CurrentMutable.InitializeReactiveUI();

            var container = builder.Build();
            resolver.SetLifetimeScope(container);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但运行时,启动失败,出现以下异常:

System.ArgumentException:“不知道如何检测 ReactiveAvalonia.HelloWorld.MainView 何时激活/停用,您可能需要实现 IActivationForViewFetcher”

Program.cs我的另一个想法是在s中进行重写Main

using System.Reflection;

using Autofac;

using Avalonia;
using Avalonia.Controls;
using Avalonia.Logging.Serilog;
using Avalonia.ReactiveUI;
using Avalonia.Threading;

using ReactiveUI;

using Splat;
using Splat.Autofac;

namespace ReactiveAvalonia.HelloWorld {

    // You may want to start here:
    // https://reactiveui.net/docs/getting-started/

    class Program {
        // http://avaloniaui.net/docs/reactiveui/
        // https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes
        public static AppBuilder BuildAvaloniaApp() {
            return AppBuilder
                .Configure<App>()
                .UseReactiveUI()
                .UsePlatformDetect()
                .LogToDebug();
        }

        private static void AppMain(Application app, string[] args) {
            app.Run(new MainView());
        }

        public static void Main(string[] args) {
            // Build a new Autofac container.
            var builder = new ContainerBuilder();
            builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces();

            // Use Autofac for ReactiveUI dependency resolution.
            // After we call the method below, Locator.Current and
            // Locator.CurrentMutable start using Autofac locator.
            AutofacDependencyResolver resolver = new AutofacDependencyResolver(builder);
            Locator.SetLocator(resolver);

            // These .InitializeX() methods will add ReactiveUI platform 
            // registrations to your container. They MUST be present if
            // you *override* the default Locator.
            Locator.CurrentMutable.InitializeSplat();
            Locator.CurrentMutable.InitializeReactiveUI();

            var container = builder.Build();
            resolver.SetLifetimeScope(container);

            BuildAvaloniaApp().Start(AppMain, args);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但这失败了,但有另一个例外:

System.Exception:“容器已经构建并设置了生命周期范围,因此无法再对其进行修改。”

我该怎么做才能让它发挥作用?

JAD*_*JAD 9

通常,Avalonia 文档会告诉您使用UseReactiveUI扩展方法才能AppBuilder使用 ReactiveUI。其作用是将一些 Avalonia 组件注册到 DI 容器:

    public static TAppBuilder UseReactiveUI<TAppBuilder>(this TAppBuilder builder)
        where TAppBuilder : AppBuilderBase<TAppBuilder>, new()
    {
        return builder.AfterPlatformServicesSetup(_ =>
        {
            RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
            Locator.CurrentMutable.RegisterConstant(new AvaloniaActivationForViewFetcher(), typeof(IActivationForViewFetcher));
            Locator.CurrentMutable.RegisterConstant(new AutoDataTemplateBindingHook(), typeof(IPropertyBindingHook));
        });
    }
Run Code Online (Sandbox Code Playgroud)

通过问题中的两种解决方案,有两种可能出现问题的方式:

设置 Autofac 在App

当您在 中设置 Autofac 时OnFrameworkInitializationCompleted,这种情况会在 中的注册设置完成后发生,本质上会覆盖它们。UseReactiveUI

如果您在覆盖 DI 容器后再次添加这些代码行,应用程序将启动:

    public override void OnFrameworkInitializationCompleted()
    {
        // Build a new Autofac container.
        var builder = new ContainerBuilder();
        builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces();

        // Use Autofac for ReactiveUI dependency resolution.
        // After we call the method below, Locator.Current and
        // Locator.CurrentMutable start using Autofac locator.
        AutofacDependencyResolver resolver = new AutofacDependencyResolver(builder);
        Locator.SetLocator(resolver);

        // These .InitializeX() methods will add ReactiveUI platform 
        // registrations to your container. They MUST be present if
        // you *override* the default Locator.
        Locator.CurrentMutable.InitializeSplat();
        Locator.CurrentMutable.InitializeReactiveUI();

        Locator.CurrentMutable.RegisterConstant(new AvaloniaActivationForViewFetcher(), typeof(IActivationForViewFetcher));
        Locator.CurrentMutable.RegisterConstant(new AutoDataTemplateBindingHook(), typeof(IPropertyBindingHook));
        RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;

        var container = builder.Build();
        resolver.SetLifetimeScope(container);
    }
Run Code Online (Sandbox Code Playgroud)

设置 Autofac 在Main

这里,在 Avalonia 尝试注册其组件之前,我们已经覆盖了 DI 容器,此时 Autofac 容器已经创建,并且它是只读的,这会导致第二个异常。同样,可以通过与其他代码一起进行相同的注册来解决此问题。那么AppBuilder.UseReactiveUI扩展方法可以省略:

using System.Reflection;

using Autofac;

using Avalonia;
using Avalonia.Controls;
using Avalonia.Logging.Serilog;
using Avalonia.ReactiveUI;
using Avalonia.Threading;

using ReactiveUI;

using Splat;
using Splat.Autofac;

namespace ReactiveAvalonia.HelloWorld {

    // You may want to start here:
    // https://reactiveui.net/docs/getting-started/

    class Program {
        // http://avaloniaui.net/docs/reactiveui/
        // https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes
        public static AppBuilder BuildAvaloniaApp() {
            return AppBuilder
                .Configure<App>()
                //.UseReactiveUI()
                .UsePlatformDetect()
                .LogToDebug();
        }

        private static void AppMain(Application app, string[] args) {
            app.Run(new MainView());
        }

        public static void Main(string[] args) {
            // Build a new Autofac container.
            var builder = new ContainerBuilder();
            builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces();

            // Use Autofac for ReactiveUI dependency resolution.
            // After we call the method below, Locator.Current and
            // Locator.CurrentMutable start using Autofac locator.
            AutofacDependencyResolver resolver = new AutofacDependencyResolver(builder);
            Locator.SetLocator(resolver);

            // These .InitializeX() methods will add ReactiveUI platform 
            // registrations to your container. They MUST be present if
            // you *override* the default Locator.
            Locator.CurrentMutable.InitializeSplat();
            Locator.CurrentMutable.InitializeReactiveUI();

            Locator.CurrentMutable.RegisterConstant(new AvaloniaActivationForViewFetcher(), typeof(IActivationForViewFetcher));
            Locator.CurrentMutable.RegisterConstant(new AutoDataTemplateBindingHook(), typeof(IPropertyBindingHook));
            RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;

            var container = builder.Build();
            resolver.SetLifetimeScope(container);
            BuildAvaloniaApp().Start(AppMain, args);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

扩展方法

现在,在类似于我们用来设置 Splat 和 ReactiveUI 的扩展方法中,这看起来会好得多:

    public static void InitializeAvalonia(this IMutableDependencyResolver resolver)
    {
        resolver.RegisterConstant(new AvaloniaActivationForViewFetcher(), typeof(IActivationForViewFetcher));
        resolver.RegisterConstant(new AutoDataTemplateBindingHook(), typeof(IPropertyBindingHook));
        RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
    }  
Run Code Online (Sandbox Code Playgroud)

    public static void Main(string[] args) {
        // Build a new Autofac container.
        var builder = new ContainerBuilder();
        builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces();

        // Use Autofac for ReactiveUI dependency resolution.
        // After we call the method below, Locator.Current and
        // Locator.CurrentMutable start using Autofac locator.
        AutofacDependencyResolver resolver = new AutofacDependencyResolver(builder);
        Locator.SetLocator(resolver);

        // These .InitializeX() methods will add ReactiveUI platform 
        // registrations to your container. They MUST be present if
        // you *override* the default Locator.
        Locator.CurrentMutable.InitializeSplat();
        Locator.CurrentMutable.InitializeReactiveUI();
        Locator.CurrentMutable.InitializeAvalonia();

        var container = builder.Build();
        resolver.SetLifetimeScope(container);
        BuildAvaloniaApp().Start(AppMain, args);
    }
Run Code Online (Sandbox Code Playgroud)