How to use a type allowed to resolve a dependency

I have several classes that accept a dependency of type ILogger . An ILogger implementation must know the type for which it is a registrar, i.e. ILogger for Foo will be new Logger(typeof(Foo)) , for Bar will be new Logger(typeof(Bar)) , etc.

I would like my own logger to be automatically entered by Unity; in other words, when I call container.Resolve<Foo>() , I want new Logger(typeof(Foo)) be injected into the Foo instance.

How can I install this in Unity? Is there a way to pass the type allowed for dependency?

(In my real code, I actually have an ILoggerFactory with a Create method that also takes a type as a parameter. So I could just pass the factory to my classes and they would call Create to get the appropriate logger, but it's not as elegant as that what I would like to achieve)


Some code to make everything clearer:

 interface ILogger { ... } class Logger : ILogger { private readonly Type _type; public Logger(Type type) { _type = type; } ... } class Foo { private readonly ILogger _logger; public Foo(ILogger logger) // here I want a Logger with its type set to Foo { _logger = logger; } } 

This related question shows exactly what I'm trying to do, and the accepted answer is exactly what I'm looking for ... but this is for NInject, not Unity.

+8
c # dependency-injection unity-container
source share
2 answers

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)); } } 
+4
source share

As @spotco suggested, I would also recommend generics. Unity registration will be done using "open generics," as shown here. This single registration allows you to enable any type in the generics Logger parameter.

 unityContainer.RegisterType(typeof(ILogger<>), typeof(Logger<>)); 

Using this registration, you can resolve the instance using a constructor like this.

 public class MyClass { public MyClass(ILogger<MyClass> logger) { ... } } 

More info here .

0
source share

All Articles