【问题标题】:Simple Injector set property whilst object graph is built构建对象图时的简单注入器集属性
【发布时间】:2012-06-22 02:42:52
【问题描述】:

使用Simple Injector 我遇到了一个与我的记录器相同的主题,我在注入记录器的服务对象的构造函数中设置记录器名称。当服务写入日志时,可以通过这个日志名轻松识别。

由于将为每个服务设置LogNames,因此每个对象图请求的记录器应该是唯一的。

我想在构建图表时自动执行此操作,我已经在ExpressionBuilt() 中四处寻找,但我正在苦苦挣扎,但还没有得到我想要的工作 - 这甚至可能(或者想要的好东西做)?

我的构造函数代码如下(LogName 属性设置代码在我的大多数服务中都很常见)。

谢谢,

克里斯

public interface ILogger
{
    void LogMessage(string message, LogLevel level,
        ILoggableCompany company = null);

    string LogName {get; set; }
}

public BusinessUnitService
{
    private readonly IUnitOfWork unitOfWork;
    private readonly ILogger logger;

    public BusinessUnitService(IUnitOfWork unitOfWork, 
        ILogger logger)
    {
        this.unitOfWork = unitOfWork;
        this.logger = logger;

        // it would be great if we could take away this 
        // line and set it automatically
        this.logger.LogName = this.GetType().ToString();
    }
}

【问题讨论】:

    标签: c# .net design-patterns dependency-injection simple-injector


    【解决方案1】:

    这个设计看起来有点像 log4net 的Logger<T> 设计,其中T 将是创建记录器的类。虽然我无法查看您的设计,但我想知道:aren't you logging too much?

    如果你真的需要这样做,至少从ILogger 接口中删除LogName 属性,因为它在那里没有业务。这意味着您必须从构造函数中删除设置此属性的代码,这绝对没问题,因为这到处都是代码重复。

    您正在尝试做的是基于上下文的注入,这不是开箱即用的支持,但 Simple Injector wiki 包含一个 Context Bases Injection section,它解释了如何添加此支持。该文档页面甚至使用Logger<T> 作为示例:-)

    使用 wiki 引用的the extension method,您可以执行以下操作:

    public interface ILogger
    {
        void LogMessage(string message, LogLevel level,
            ILoggableCompany company = null);
    
        // No LogName property here. Keep it clean.
    }
    
    public class LoggerImpl : ILogger
    {
        public void LogMessage(string message, 
            LogLevel level, ILoggableCompany company)
        {
           // implementation
        }
    
        // Property only part of the implementation.
        public string LogName {get; set; }
    }
    
    // The parent contains information about the type in 
    // which ILogger is injected.
    container.RegisterWithContext<ILogger>(parent =>
    {
        // Retrieve a new LoggerImpl instance from the container 
        // to allow this type to be auto-wired.
        var logger = container.GetInstance<LoggerImpl>();
    
        // ImplementationType is null when ILogger is
        // requested directly (using GetInstance<ILogger>())
        // since it will have no parent in that case.
        if (parent.ImplementationType != null)
        {
            // Set the LogName with the name of the class 
            // it is injected into.
            logger.LogName = parent.ImplementationType.Name;
        }
    
        return logger;
    });
    
    // Register the LoggerImpl as transient. This line is
    // in fact redundant in Simple Injector, but it is important
    // to not accidentally register this type with another
    // lifestyle, since the previous registration depends on it
    // to be transient.
    container.Register<LoggerImpl>();
    

    因为这是通过挂钩 BuiltExpression 事件并通过重新连接表达式来实现的,以这种方式解析实例的速度几乎与使用 Func&lt;T&gt; 工厂方法的注册一样快。

    【讨论】:

    • 但这仅适用于构造函数注入,不适用于属性注入,对吗?因为属性设置器隐藏在函数调用后面,所以它们不会出现在实例化表达式中。这意味着表达式重写器无法发现正在注入的属性。
    • @FyodorSoikin:它实际上可以很好地与属性注入一起工作,因为属性依赖关系确实出现在实例化表达式中。自己试试吧。
    • 确实如此,我错了。即使属性本身没有出现在表达式中,对rootFactory 的调用也会出现,这就是您可以捕获的。但是,我发现了另一个问题:当服务是开放泛型时,parent.ImplementationType 最终成为 服务类型,而不是实现类型,这意味着您不能真正使用泛型进行基于上下文的注入服务。
    • @FyodorSoikin:确实,属性的设置不会在表达式树中结束,但属性的依赖项的创建会,并且故意构建表达式树以允许这样做。 Simple Injector 甚至包含确保这种行为不会改变的单元测试。
    • 完成。结果你甚至不需要开放的通用服务,但你确实需要属性注入。 github.com/simpleinjector/SimpleInjector/issues/86
    猜你喜欢
    • 1970-01-01
    • 2011-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多