繁体   English   中英

简单注入器显式属性注入 - 属性是 null 内部构造函数

[英]Simple Injector explicit attribute injection - property is null inside constructor

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

Main调用的 static RunApplication中的 DI 容器请求System的一个实例:

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属性,没有变化。 还尝试将记录器实例化为 singleton,行为相同。

真的很感激任何想法,我在搜索论坛、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 => typeof(NLogLogger<>)
            .MakeGenericType(c.Consumer.ImplementationType),
        Lifestyle.Singleton,
        c => true);

    // UI registration
    container.Register<MainWindow>();
    //container.Register<MainWindowViewModel>();

    container.Register<ISystem, System>(Lifestyle.Singleton);

    container.Verify();
    return container;
}

更新,显式属性注入工作正常。 它发生在施工后。 我想这是有设计原因的,尽管在某种程度上与我的想法相反,属性注入将按需/首次使用时执行。

计划进行更多试验,以查看在解决属性依赖关系的时间上可用的控制。 如果任何更有经验的人对此有任何建议或可以向我指出其他文档,我会欢迎它。 装饰器听起来像是一种更优雅的方式,可以确保记录器按预期可用,并允许独立延迟加载被装饰者的关注点。 这里有一些讨论:

SimpleInjector - “懒惰”实例化一个依赖于首次使用的 singleton

您正在使用System构造函数Logger属性。 然而,属性仅在构造函数完成后才被初始化。 如果您从等式中删除 Simple Injector,并回退到普通的旧 C#,您会看到相同的结果。 例如:

var system = new System() // <-- constructor call
{
    Logger = new NLogLogger<System>() // Logger_set is called after the ctor
};

如果您运行此代码,您将看到System的构造函数抛出相同的NullReferenceException

这意味着您不应该使用构造函数内部的任何属性。 更广泛地说,从 DI 的角度来看,您不应该在构造函数内部(或在构造过程中)使用任何服务,正如 Mark Seemann 在此处所描述的那样。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM