简体   繁体   English

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

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

New to Simple Injector, trying to get some pieces working for a prototype. Simple Injector 的新手,试图让一些部件为原型工作。 I am creating a WPF application that uses Simple Injector and ReactiveUI, but can't seem to get explicit property injection via attribute to trigger.我正在创建一个使用 Simple Injector 和 ReactiveUI 的 WPF 应用程序,但似乎无法通过属性来触发显式属性注入。 The specific example I am working through is just testing injection of a logger.我正在处理的具体示例只是测试记录器的注入。 The plan is to roll this into a decorator, but I have run across the need for attribute injection with previous projects/DI libraries.计划是将其滚动到装饰器中,但我已经遇到了使用以前的项目/DI 库进行属性注入的需要。 Just want to verify I am able to use it.只是想验证我能够使用它。

Snippet of the bootstrapping:引导的片段:

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

An instance of the System is requested from the DI container in the static RunApplication called from Main :Main调用的 static RunApplication中的 DI 容器请求System的一个实例:

var system = container.GetInstance<ISystem>();

And here is the property injection in the system:这是系统中的属性注入:

public class System : ISystem
{
    [Import] public ILogger Logger { get; set; }

    public System()
    {
        // Logger is null here. NullReferenceException is thrown
        Logger.LogInfo("Creating System");
    }
}

At this point in the constructor, the Logger property is null and attempt to log fails with exception.此时在构造函数中, Logger属性为 null 并尝试记录失败并出现异常。 I should mention the ILogger is my own abstraction of NLog.我应该提到ILogger是我自己对 NLog 的抽象。 If I instead perform constructor injection:如果我改为执行构造函数注入:

public System(ILogger logger)

Simple Injector picks up on this and resolves the dependency fine. Simple Injector 接受了这一点并很好地解决了依赖关系。 I have tried changing the Import attribute to a different custom-defined Dependency attribute, no change.我尝试将Import属性更改为不同的自定义Dependency属性,没有变化。 Have also tried just instantiating the logger as a singleton, same behavior.还尝试将记录器实例化为 singleton,行为相同。

Really appreciate any ideas, I'm running dry on searching forums, the SimpleInjector/ReactiveUI docs, and Steven's DI book.真的很感激任何想法,我在搜索论坛、SimpleInjector/ReactiveUI 文档和 Steven 的 DI 书上都快干了。

Edit - here is the PropertySelectionBehavior code as well:编辑 - 这里也是 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> { }

2nd Edit - I can take out all of the initialization related to ReactiveUI and still reproduce same behavior.第二次编辑 - 我可以取出所有与 ReactiveUI 相关的初始化并仍然重现相同的行为。 New sample looks like:新样本如下所示:

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

Update, the explicit property injection is working fine.更新,显式属性注入工作正常。 It occurs after construction.它发生在施工后。 I imagine there are design reasons for this, although somehow it was contrary to my mental model that the property injection would be performed on-demand/on first use.我想这是有设计原因的,尽管在某种程度上与我的想法相反,属性注入将按需/首次使用时执行。

Planning on experimenting a bit more to see what control is available over the timing to resolve property dependencies.计划进行更多试验,以查看在解决属性依赖关系的时间上可用的控制。 If anyone who is more experienced has any advice on that or can point me to additional documentation I would welcome it.如果任何更有经验的人对此有任何建议或可以向我指出其他文档,我会欢迎它。 The decorator sounds like the more elegant way to make sure the logger is available as expected and allow independent lazy loading of decoratee concerns.装饰器听起来像是一种更优雅的方式,可以确保记录器按预期可用,并允许独立延迟加载被装饰者的关注点。 Some discussion here:这里有一些讨论:

SimpleInjector - "Lazy" Instantiate a singleton that has dependencies on first use SimpleInjector - “懒惰”实例化一个依赖于首次使用的 singleton

You are using the Logger property from inside System 's constructor.您正在使用System构造函数Logger属性。 Properties, however, are only initialized after the constructor finished.然而,属性仅在构造函数完成后才被初始化。 If you remove Simple Injector from the equation, and fallback to plain old C#, you would see the same.如果您从等式中删除 Simple Injector,并回退到普通的旧 C#,您会看到相同的结果。 For instance:例如:

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

If you run this code, you will see the same NullReferenceException thrown by the constructor of System .如果您运行此代码,您将看到System的构造函数抛出相同的NullReferenceException

What this means is that you shouldn't use any properties from inside your constructor.这意味着您不应该使用构造函数内部的任何属性。 Even more broadly, from a DI perspective, you shouldn't use any service inside your constructor (or during construction for that matter) as is described by Mark Seemann here .更广泛地说,从 DI 的角度来看,您不应该在构造函数内部(或在构造过程中)使用任何服务,正如 Mark Seemann 在此处所描述的那样。

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

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