Lets imagine following requirement: we need to inject NLog logger into our component and set logger's name according to our component type. Simple but arguable solution is to inject implementation of service locator pattern, namely Nlog's LogFactory class, but it introduces repeatable code and clogs our components with extra responsibility of creating loggers for themselves.
Good news is that it is possible to group functionality for creating loggers into a single place and inject right logger straight into constructor. Consider following component class:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] | |
public class TypedLoggerAttribute : Attribute | |
{ | |
} | |
[TypedLogger] | |
public class Worker | |
{ | |
private readonly Logger logger_; | |
public Worker(Logger logger) | |
{ | |
logger_ = logger; | |
} | |
public void Start() | |
{ | |
logger_.Info("Starting work"); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class TypedLoggersInjector : Module | |
{ | |
private readonly LogFactory logFactory_; | |
public TypedLoggersInjector(LogFactory logFactory) | |
{ | |
logFactory_ = logFactory; | |
} | |
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration) | |
{ | |
var reflectionActivator = registration.Activator as ReflectionActivator; | |
if (reflectionActivator != null && reflectionActivator.LimitType.GetCustomAttributes(typeof(TypedLoggerAttribute), false).Length > 0) | |
{ | |
var targetType = reflectionActivator.LimitType; | |
var namedParameters = reflectionActivator.ConstructorFinder.FindConstructors(targetType) | |
.SelectMany(c => c.GetParameters().Where(p => p.ParameterType == typeof(Logger))) | |
.Select(p => new NamedParameter(p.Name, logFactory_.GetLogger(targetType.FullName))).ToList(); | |
if (namedParameters.Count > 0) | |
{ | |
registration.Preparing += (sender, args) => | |
{ | |
args.Parameters = args.Parameters.Concat(namedParameters); | |
}; | |
} | |
} | |
base.AttachToComponentRegistration(componentRegistry, registration); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static IContainer ConfigureContainer() | |
{ | |
var builder = new ContainerBuilder(); | |
builder.RegisterType<Worker>(); | |
builder.RegisterModule(new TypedLoggersInjector(new LogFactory())); | |
return builder.Build(); | |
} | |
public static void TestInjection() | |
{ | |
var container = ConfigureContainer(); | |
var worker = container.Resolve<Worker>(); | |
worker.Start(); | |
} |