This adds a container extension that will set the Type parameter of the Logger constructor to the type into which ILogger is entered.
The transition method IBuilderContext.Policies is used to store the type into which ILogger is entered.
It may be harder than necessary, but it seems to work
public class LoggerExtension : UnityContainerExtension { public static NamedTypeBuildKey LoggerBuildKey = new NamedTypeBuildKey<Logger>(); protected override void Initialize() { Context.Strategies.Add(new LoggerTrackingPolicy(), UnityBuildStage.TypeMapping); Context.Strategies.Add(new LoggerBuildUpStrategy(), UnityBuildStage.PreCreation); } } public class LoggerTrackingPolicy : BuilderStrategy { public LoggerTrackingPolicy() { } public override void PreBuildUp(IBuilderContext context) { if (context.BuildKey.Type != typeof(Logger)) { var loggerPolicy = context.Policies.Get<ILoggerPolicy>(LoggerExtension.LoggerBuildKey); if (loggerPolicy == null) { loggerPolicy = new LoggerPolicy(); context.Policies.Set<ILoggerPolicy>(loggerPolicy, LoggerExtension.LoggerBuildKey); } loggerPolicy.Push(context.BuildKey.Type); } } } public class LoggerBuildUpStrategy : BuilderStrategy { public LoggerBuildUpStrategy() { } public override void PreBuildUp(IBuilderContext context) { if (context.BuildKey.Type == typeof(Logger)) { var policy = context.Policies.Get<ILoggerPolicy>(LoggerExtension.LoggerBuildKey); Type type = policy.Peek(); if (type != null) { context.AddResolverOverrides(new ParameterOverride("type", new InjectionParameter(typeof(Type), type))); } } } public override void PostBuildUp(IBuilderContext context) { if (context.BuildKey.Type != typeof(Logger)) { var policy = context.Policies.Get<ILoggerPolicy>(LoggerExtension.LoggerBuildKey); policy.Pop(); } } } public interface ILoggerPolicy : IBuilderPolicy { void Push(Type type); Type Pop(); Type Peek(); } public class LoggerPolicy : ILoggerPolicy { private Stack<Type> types = new Stack<Type>(); public void Push(Type type) { types.Push(type); } public Type Peek() { if (types.Count > 0) { return types.Peek(); } return null; } public Type Pop() { if (types.Count > 0) { return types.Pop(); } return null; } }
How it works: when a type that is not Logger tries to be resolved, then at the TypeMapping stage (before any creation) the type is pushed onto the stack. Later, before creating, if the type is Logger, then the type that it injects peeks from the stack, and this type is used as a permission override. After creation, if the type is not Logger, it is pushed out of the stack.
And some code to make sure it works (I added the Type property to the logger to make sure it is set correctly):
class Program { static void Main(string[] args) { IUnityContainer container = new UnityContainer(); container.RegisterType<ILogger, Logger>(); container.AddNewExtension<LoggerExtension>(); var a = container.Resolve<A>(); var b = container.Resolve<B>(); var c = container.Resolve<C>(); var d = container.Resolve<D>(); var x = container.Resolve<X>(); } } public interface ILogger { Type Type { get; } } public class Logger : ILogger { private readonly Type _type; public Logger(Type type) { _type = type; } public Type Type { get { return _type; } } } public class A { public A(ILogger logger) { System.Diagnostics.Debug.Assert(logger.Type == typeof(A)); } } public class B { public B(ILogger logger) { System.Diagnostics.Debug.Assert(logger.Type == typeof(B)); } } public class C { public C(A a, D d, B b, ILogger logger) { System.Diagnostics.Debug.Assert(logger.Type == typeof(C)); } } public class D { public D() { } } public class X { public X(Y y) { } } public class Y { public Y(ILogger logger) { System.Diagnostics.Debug.Assert(logger.Type == typeof(Y)); } }
Randy levy
source share