Cook Computing

« March 2009 »

Injected Type Needs to Know Type Being Injected Into

Injecting a logger into an object is a frequently used example of DI. I have a case where the logger I want to inject needs to know the type of the object it is being injected into, so that as part of its output it can log the type from which each log line is being output. For example in the code below, where the constructor of type Foo has a parameter of type ILogger and where the implementation of ILogger - the Logger class - has a Type parameter.

class Logger : ILogger
{
    Type _loggerClientType;

    public Logger(Type loggerClientType)
   {
        _loggerClientType = loggerClientType;
   }

    // ...
}

class Foo
{
    ILogger _logger;

    public Foo(ILogger logger)
    {
        _logger = logger;
    }

    // ...
}

I posted to the Unity discussion forum, asking if there any way of achieving this with Unity. Dan Piessens replied:

The short answer is yes, but not without writing some serious Unity extensions. You'd need to override the default parameter and property resolvers, create a resolver that gets and stores the type of the object being resolved and then adds the type into the build stack before resolution then removes it. We got it all working with an set of extensions we wrote, but it's pretty complicated and unfortunately our IP rules don't allow us to release that code.

I replied:

If I get the time I may look into doing something similar to what you have done. For now I have made the ILogger members static and marked them with a custom attribute so that at startup I can scan all the types in the assembly and initialize any attributed ILogger members I find.

Posted by Charles Cook at 06:26 PM. Permalink. View Comments.

Logging of Unity Resolution Failure

When using the Unity Dependency Injection container I've found that when a resolution of a dependency fails the Message property of the resulting ResolutionFailed exception is not immediately useful if there is more than one level of dependencies being resolved. For example this code:

using System;
using System.Collections.Generic;
using Microsoft.Practices.Unity;

interface ILogger
{
}

class Foo
{
  public Foo(ILogger logger)
  {
  }
}

class Bar
{
  public Bar(Foo foo)
  {
  }
}

class Program
{
  static void Main(string[] args)
  {
    var unity = new UnityContainer();
    unity.Resolve(typeof(Bar));
  }
}

Results in the following exception message:

Resolution of the dependency failed, type = "Bar", name = "". Exception message is: The current build operation (build key Build Key[Bar, null]) failed: The parameter foo could not be resolved when attempting to call constructor Bar(Foo foo). (Strategy type BuildPlanStrategy, index 3)

The information describing why the resolution failed is hidden in the hierachy of inner exceptions. To make this easy to read I use code like the LogResolutionFailed method (just writing to the console in this example):

class Program
{
  static void Main(string[] args)
  {
    try
    {
      var unity = new UnityContainer();
      unity.Resolve(typeof(Bar));
    }
    catch (ResolutionFailedException rfex)
    {
      LogResolutionFailed(rfex);
    }
  }

  static void LogResolutionFailed(ResolutionFailedException rfex)
  {
    Console.WriteLine("ResolutionFailedException for {0} {1}", 
      rfex.TypeRequested, rfex.NameRequested);
    var messages = new Stack<string>();
    var inner = rfex.InnerException;
    while (inner != null)
    {
      if (inner is InvalidOperationException)
        Console.WriteLine(inner.Message);
      inner = inner.InnerException;
    }
  }
}

Results in the following output which makes the cause of the resolution failure more obvious:

ResolutionFailedException for Bar

The parameter foo could not be resolved when attempting to call constructor Bar(Foo foo).

The parameter logger could not be resolved when attempting to call constructor Foo(ILogger logger).

The current type, ILogger, is an interface and cannot be constructed. Are you missing a type mapping?

I'm currently working on a debugger visualizer for ResolutionFailedException so that the same information can be seen in the debugger, instead of having to drill down into the inner exceptions.

Posted by Charles Cook at 08:20 AM. Permalink. View Comments.