问题描述
Simple Injector 的新手,试图让一些部件为原型工作。我正在创建一个使用 Simple Injector 和 ReactiveUI 的 WPF 应用程序,但似乎无法通过属性来触发显式属性注入。我正在处理的具体示例只是测试记录器的注入。计划是将其滚动到装饰器中,但我遇到了对以前的项目/DI 库进行属性注入的需求。只是想验证我是否能够使用它。
自举代码片段:
private Container RegisterDependencies(Container container = null)
{
container ??= new Container();
// Container initialization that must precede dependency registration
// occurs here
// Enable property injection via the [Import] attribute
container.Options.PropertySelectionBehavior =
new ImportPropertySelectionBehavior();
SimpleInjectorInitializer initializer = new SimpleInjectorInitializer();
Locator.SetLocator(initializer);
Locator.CurrentMutable.InitializeSplat();
Locator.CurrentMutable.InitializeReactiveUI();
container.UseSimpleInjectorDependencyResolver(initializer);
container.RegisterConditional(
typeof(ILogger),c => typeof(NLogLogger<>)
.MakeGenericType(c.Consumer.ImplementationType),Lifestyle.Singleton,c => true);
container.Register<MainWindow>();
container.Register<ISystem,System>(Lifestyle.Singleton);
container.Verify();
return container;
}
从 System
调用的静态 RunApplication
中的 DI 容器请求 Main
的实例:
var system = container.GetInstance<ISystem>();
这里是系统中的属性注入:
public class System : ISystem
{
[Import] public ILogger Logger { get; set; }
public System()
{
// Logger is null here. NullReferenceException is thrown
Logger.LogInfo("Creating System");
}
}
此时在构造函数中,Logger
属性为 null,尝试记录失败并出现异常。我应该提到 ILogger
是我自己对 NLog 的抽象。如果我改为执行构造函数注入:
public System(ILogger logger)
Simple Injector 注意到这一点并很好地解决了依赖关系。我尝试将 Import
属性更改为不同的自定义 Dependency
属性,但没有任何更改。还尝试将记录器实例化为单例,行为相同。
非常感谢您的任何想法,我在搜索论坛、SimpleInjector/ReactiveUI 文档和 Steven 的 DI 书上都快干掉了。
编辑 - 这里也是 PropertySelectionBehavior 代码:
public class PropertySelectionBehavior<T> : IPropertySelectionBehavior
where T : Attribute
{
public bool SelectProperty(
Type implementationType,PropertyInfo propertyInfo) =>
propertyInfo.GetCustomAttributes(typeof(T)).Any();
}
public class ImportPropertySelectionBehavior :
PropertySelectionBehavior<ImportAttribute> { }
第二次编辑 - 我可以删除与 ReactiveUI 相关的所有初始化并仍然重现相同的行为。新示例如下:
private Container RegisterDependencies(Container container = null)
{
container ??= new Container();
container.Options.PropertySelectionBehavior =
new ImportPropertySelectionBehavior();
// Logger registration
container.RegisterConditional(
typeof(ILogger),c => true);
// UI registration
container.Register<MainWindow>();
//container.Register<MainWindowviewmodel>();
container.Register<ISystem,System>(Lifestyle.Singleton);
container.Verify();
return container;
}
解决方法
更新,显式属性注入工作正常。它发生在施工后。我想这有设计原因,尽管不知何故,这与我的心理模型相反,即按需/首次使用时执行属性注入。
计划进行更多试验,以了解对解决属性依赖关系的时间进行哪些控制。如果任何更有经验的人对此有任何建议或可以向我指出其他文档,我会欢迎。装饰器听起来像是确保记录器按预期可用并允许独立延迟加载被装饰者关注点的更优雅的方式。这里有一些讨论:
SimpleInjector - "Lazy" Instantiate a singleton that has dependencies on first use
,您正在使用 inside Logger
的构造函数中的 System
属性。但是,属性仅在构造函数完成后 初始化。如果您从等式中删除 Simple Injector,并回退到普通的旧 C#,您会看到相同的结果。例如:
var system = new System() // <-- constructor call
{
Logger = new NLogLogger<System>() // Logger_set is called after the ctor
};
如果您运行此代码,您将看到 NullReferenceException
的构造函数抛出的相同 System
。
这意味着您不应使用构造函数内部的任何属性。更广泛地说,从 DI 的角度来看,您不应像 Mark Seemann here 所描述的那样在构造函数内(或在构造过程中)使用任何服务。