Using Simple Injector I have come across a common theme with my logger where I set the logger name in the constructor of the service object to which the logger is injected. When the service writes to the log, it can be easily identified by this logname.

Since LogNames will be set for each service, the logger should be unique per object graph request.

I would like to do this automatically when the graph is being built, I have poked around in ExpressionBuilt() but I am struggling and yet to get what i want to work - is this even possible (or an OK thing to want to do)?

My constructor code is below (this LogName property setting code is common across most of my services).

Thanks,

Chris

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();
    }
}
有帮助吗?

解决方案

This design looks a bit like the Logger<T> design of log4net, where T will be the class the logger is created for. Although I can't look into your design, but I'm wondering: aren't you logging too much?

If you really need to do this, at least remove the LogName property from the ILogger interface, since it has no business there. This means you have to remove the code that sets this property from your constructors, which is absolutely fine, because this is code duplication all over the place.

What you are trying to do is context based injection, which is not something that is supported out of the box, but the Simple Injector wiki contains a Context Bases Injection section that explains how add this support. This documentation page even uses a Logger<T> as example :-)

Using the extension method the wiki refers to, you can do the following:

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>();

Because this works by hooking into the BuiltExpression event, and by rewiring the expressions, resolving instances this way this almost as fast as registrations using a Func<T> factory method.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top