XAML 使用`Application.LoadComponent`进行依赖注入

fwzugrvs  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(109)

我在F#项目中有一个XAML文件。XAML文件是作为资源构建的(而不是BAML编译的页面)。
为了动态加载页面,我使用Application.LoadComponent方法和适当的URI来查找资源。
此外,为了专门化组件(例如Window),我创建了一个自定义的MainWindow类,它继承自Window

type MainWindow() =
    inherit Window()

字符串
当通过Microsoft.Extensions.DependencyInjection引入依赖注入时,现在出现了问题。问题是LoadComponent不知道如何使用我定制的DI容器为MainWindow注入构造函数参数(例如,它可能需要FooService):

type MainWindow(foo: FooService) =
    inherit Window()


事实上,WPF对这个外部服务提供者一无所知,因为我没有告诉它。
是否有某种WPF魔术可以用来挂钩到LoadComponent机器,并确保依赖注入工作?
这在C#中不是一个问题,BAML编译确保存在一个包含XAML和代码隐藏的内聚编译类,并且程序员不需要关心手动加载XAML。
在F#中,情况就不同了,我不知道如何将依赖注入引入到WPF机制中。由于Application.LoadComponent没有任何重载来提供服务提供者,我想知道是否有其他机制可以做到这一点。
我的后备方法是在MainWindow类初始化代码中获取对服务提供者的全局引用,例如:

type MainWindow() =

    inherit Window()

    let servicerProvider = AppHost.Services
    let foo = serviceProvider.GetRequiredService<FooService>()


对于这个用例,整个DI业务似乎有点冒险,特别是对于子窗口,但现在我正在追求这条道路,因为我已经开始使用的框架也在使用它。

yrdbyhpb

yrdbyhpb1#

关键是整个XAML引擎都是围绕这样一个约束构建的:所有被引擎示例化的类型都必须声明一个默认构造函数,因为这是引擎将调用的唯一构造函数。没有任何类型信息,引擎如何知道如何构造一个类型来满足构造函数依赖性?
相反,XAML中使用的类型通常通过依赖属性基础设施(例如属性默认值或属性继承)进行 * 初始化 *,并使用数据绑定或通过定义Style或通过本地设置属性进行 * 配置 *。

  • “我的后备方法是在MainWindow类初始化代码中获取对服务提供程序的全局引用”*

这是一个糟糕的想法,而且不是 Dependency Injection。这是 *Service Injection *,在应用 * DepartmentInjection * 模式时使用 Singleton 或 *Service Injection * 设计模式是完全没有意义的,并且增加了绝对冗余的复杂性,因为它们有效地抵消了 * DepartmentInjection * 的效率。依赖注入 * 通常用于消除这两种反模式。
让所有东西都是静态的违反了很多好的和重要的原则,并且肯定会导致有味道的代码。因为引用的变量是静态的,所以遵循你的模式会让你的整个应用程序有味道。
当你想共享IoC容器时,不要这样做。相反,使用工厂。总是这样。

  • 抽象工厂 * 是一个强大的创造性设计模式,解决了许多与类型示例化相关的问题。

建议的解决方案是:
1.将特定类型的Application.LoadComponent调用 Package 到专用工厂中。
1.您可以将XAML类型的依赖项定义为此工厂的依赖项。
1.然后,您可以像其他类型一样向IoC容器注册工厂,以便IoC容器解析和提供所有依赖项。如果其中一些依赖项不是共享示例,则工厂本身必须接收专用工厂,以便动态创建XAML类型的 transient 依赖项的示例。

  • 接下来,通过将依赖项赋给Application.LoadComponent返回的示例,
  • a)自定义InitializeComponent方法,由XAML类型声明,定义通常由构造函数定义的所有依赖项(推荐)
  • B)或公共财产。

你不应该仅仅为了这个目的而将属性声明为public。如果这些属性必须隐藏,那么使用变量 a)

  • a)* 是最优雅的解决方案,因为它不需要工厂知道关于类型的任何细节(越少越好)。工厂只需提供初始化方法的参数列表。
  • 将正确构造的类型添加到可视化树中并使用它。

另一种解决方案是将 all 依赖注入到MainWindow中,并使其通过只读依赖属性公开它们,然后让UI元素绑定到这些属性以获取它们的依赖。这可能适用于小场景,特别是依赖是共享示例的情况,但这通常违反常见的数据隐藏原则。
对于您的特定情况, Package Application.LoadComponent调用的每个XAML类型的工厂是最干净和最常见的方式。

  • “整个DI业务对于这个用例来说似乎有点冒险,特别是对于子窗口”*

现在您知道,您可以通过为创建有问题的类型注入工厂来解决所有这类问题。
例如,当父Window接收到创建子Window的工厂(例如对话框)时,所有的依赖项和配置细节都被隐藏并封装在这个工厂中。父可以创建子的类型,而不必依赖于子的依赖项,只是委托它们。

  • 抽象工厂 * 是 * 依赖注入 * 设计模式的无所不在的伴侣。

相关问题