我在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业务似乎有点冒险,特别是对于子窗口,但现在我正在追求这条道路,因为我已经开始使用的框架也在使用它。
1条答案
按热度按时间yrdbyhpb1#
关键是整个XAML引擎都是围绕这样一个约束构建的:所有被引擎示例化的类型都必须声明一个默认构造函数,因为这是引擎将调用的唯一构造函数。没有任何类型信息,引擎如何知道如何构造一个类型来满足构造函数依赖性?
相反,XAML中使用的类型通常通过依赖属性基础设施(例如属性默认值或属性继承)进行 * 初始化 *,并使用数据绑定或通过定义
Style
或通过本地设置属性进行 * 配置 *。这是一个糟糕的想法,而且不是 Dependency Injection。这是 *Service Injection *,在应用 * DepartmentInjection * 模式时使用 Singleton 或 *Service Injection * 设计模式是完全没有意义的,并且增加了绝对冗余的复杂性,因为它们有效地抵消了 * DepartmentInjection * 的效率。依赖注入 * 通常用于消除这两种反模式。
让所有东西都是静态的违反了很多好的和重要的原则,并且肯定会导致有味道的代码。因为引用的变量是静态的,所以遵循你的模式会让你的整个应用程序有味道。
当你想共享IoC容器时,不要这样做。相反,使用工厂。总是这样。
建议的解决方案是:
1.将特定类型的
Application.LoadComponent
调用 Package 到专用工厂中。1.您可以将XAML类型的依赖项定义为此工厂的依赖项。
1.然后,您可以像其他类型一样向IoC容器注册工厂,以便IoC容器解析和提供所有依赖项。如果其中一些依赖项不是共享示例,则工厂本身必须接收专用工厂,以便动态创建XAML类型的 transient 依赖项的示例。
Application.LoadComponent
返回的示例,InitializeComponent
方法,由XAML类型声明,定义通常由构造函数定义的所有依赖项(推荐)你不应该仅仅为了这个目的而将属性声明为
public
。如果这些属性必须隐藏,那么使用变量 a)。另一种解决方案是将 all 依赖注入到
MainWindow
中,并使其通过只读依赖属性公开它们,然后让UI元素绑定到这些属性以获取它们的依赖。这可能适用于小场景,特别是依赖是共享示例的情况,但这通常违反常见的数据隐藏原则。对于您的特定情况, Package
Application.LoadComponent
调用的每个XAML类型的工厂是最干净和最常见的方式。现在您知道,您可以通过为创建有问题的类型注入工厂来解决所有这类问题。
例如,当父
Window
接收到创建子Window
的工厂(例如对话框)时,所有的依赖项和配置细节都被隐藏并封装在这个工厂中。父可以创建子的类型,而不必依赖于子的依赖项,只是委托它们。