【问题标题】:How to use Autofac as DI container in Avalonia.ReactiveUI?如何在 Avalonia.ReactiveUI 中使用 Autofac 作为 DI 容器?
【发布时间】:2021-03-14 13:16:45
【问题描述】:

我正在尝试使用 Autofac 覆盖在 Avalonia 和 ReactiveUI 中使用的 DI 容器。到目前为止,我已经尝试关注instructions in the Splat.Autofac repository,但我无法让 Avalonia 工作。

作为一个工作示例,我采用了HelloWorld example from the ReactiveUI.Samples 存储库。

我可以想到两个地方来覆盖 Splat。在App.xaml.csOnFrameworkInitializationCompleted 方法中:

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);
        }
    }
}

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

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

我的另一个想法是在Program.csMain 中进行覆盖:

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);
        }
    }
}

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

System.Exception: '容器已经构建并且生命周期范围设置,所以不能再修改它。'

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

【问题讨论】:

    标签: c# autofac reactiveui avaloniaui


    【解决方案1】:

    通常,Avalonia 文档会告诉您使用 AppBuilder 上的 UseReactiveUI extension method 才能使用 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));
            });
        }
    

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

    App 中设置 Autofac

    当您在 OnFrameworkInitializationCompleted 中设置 Autofac 时,这会发生在 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);
        }
    

    Main 中设置 Autofac

    在这里,我们已经在 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);
            }
        }
    }
    

    扩展方法

    现在,在类似于我们用来设置 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;
        }  
    

        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);
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-03
      • 1970-01-01
      • 2020-06-09
      • 2020-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多