Tripping over logs: A story of Unity - Part 3

Recap + Intro

In part 1 we looked at the problem with just adding logging code and how it quickly gets messy, and we looked at how the more loosely coupled the code is, the easier it is to effect changes painlessly. Then in part 2 we took that code and added Unity, which gave us control via a config file as to what class our logging should use. Now we are going to look at a benefit of having this Mary Poppins bag of magic controlled for us.

Note you can get Unity with the Enterprise Library from p&p group.

As I said in part 1:

What I am going to do is look at a practical approach to using Unity. I will not be going into DI or IoC—those topics have been covered much better by people smarter than me. If you want the theory, look elsewhere. If you want to get running with Unity, this is for you.

This is a multi-part series, so here is the series guide in case you are looking for the rest:


The time of your life

In our code we created a global variable for the logging and just called that—but that isn’t always a good idea. Logging is a poor example of this; security is a better one. If you wanted to do a security check in some methods but not others, why waste time with a global variable? Why not reach into the bag each time it’s needed and pull one out? The problem can come in when performance drops because you’re calling the constructor each time you need the object. The reality is, this issue was solved years ago with the Singleton pattern.

So can we apply that to Unity? Yes—Unity is smart enough to give us a way to do that thanks to its built-in lifetime management! To demo this, I’ve modified the code from part 2 as follows:

class Program
{
  static IUnityContainer container = new UnityContainer();

  static void DoSomething(string Username)
  {
    ILogger logger = container.Resolve<ILogger>();
    logger.LogThis("DoSomething Called with Username Parameter set to:" + Username);
    Console.WriteLine("Hello {0}", Username);
    logger.LogThis("DoSomething Completed");
  }

  static void Main(string[] args)
  {
    UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
    section.Containers.Default.Configure(container);
    ILogger logger = container.Resolve<ILogger>();
    logger.LogThis("Application started");
    DoSomething("Robert");
    Console.ReadKey();
    logger.LogThis("Application Completed");
  }
}

public interface ILogger
{
  void LogThis(string Message);
}

public class DebugLogger : ILogger
{
  public void LogThis(string Message)
  {
    System.Diagnostics.Debug.WriteLine(String.Format("{0}: {1}", DateTime.Now, Message));
  }
}

public class ConsoleLogger : ILogger
{
  public ConsoleLogger()
  {
    Console.WriteLine("I iz in yourz codez, slowingz its downz");
    Thread.Sleep(5000);
  }

  public void LogThis(string Message)
  {
    Console.WriteLine("{0}: {1}", DateTime.Now, Message);
  }
}

What happens if my code runs slowly now and the Lolzcat message appears twice (since I created the object twice)? If I assume that it’s just an example of the overhead of the constructor—which it is—and that I need to live with it at least once, implementing the singleton pattern is the solution. To do this actually means two lines of changes to the app.config (one line if you want to be messy). First, I add a new type alias for the _ContainerControlledLifetimeManager_, which I’ve called singleton (line 11 below). Then, I expand my type with a lifetime tag linking back to it (line 18 below).

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
  </configSections>

  <unity>
    <typeAliases>
      <typeAlias alias="Logger" type="BigSystem.ConsoleLogger, BigSystem" />
      <typeAlias alias="ILogger" type="BigSystem.ILogger, BigSystem" />
      <typeAlias alias="singleton" type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager, Microsoft.Practices.Unity" />
    </typeAliases>
    
    <containers>
      <container>
        <types>
          <type type="ILogger" mapTo="Logger">
            <lifetime type="singleton" />
          </type>
        </types>
      </container>
    </containers>
   </unity>
</configuration>

Now I just rerun my application, and it runs twice as fast with only one Lolzcat message, since the container (the magic bag) has now created the object once. When I ask for it a second time, it provides the already created one! This is an amazingly useful system because it’s per mapping, and out of the box are three lifetime managers, and you can build your own.

For the sake of ease, here’s a copy-paste from the documentation on those three lifetime managers:

ContainerControlledLifetimeManager Unity returns the same instance of the registered type or object each time you call the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes. This lifetime manager effectively implements a singleton behavior for objects. Unity uses this lifetime manager by default for the RegisterInstance method if you do not specify a different lifetime manager. If you want singleton behavior for an object that Unity creates when you use the RegisterType method, you must explicitly specify this lifetime manager. The behavior is as follows:

  • If you used the RegisterType method to register a type, Unity creates a new instance of the registered type during the first call to the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes. Subsequent requests return the same instance.
  • If you used the RegisterInstance method to register an existing object, Unity returns this instance every time you call the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes.

ExternallyControlledLifetimeManager This lifetime manager allows you to register type mappings and existing objects with the container so that it maintains only a weak reference to the objects it creates when you call the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes based on attributes or constructor parameters within that class. Unity returns the same instance of the registered type or object each time you call the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes. However, the container does not hold onto a strong reference to the object after it creates it, which means the garbage collector can dispose of the object if no other code is holding a strong reference to it.

PerThreadLifetimeManager Unity returns, on a per-thread basis, the same instance of the registered type or object each time you call the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes. This lifetime manager effectively implements a singleton behavior for objects on a per-thread basis. PerThreadLifetimeManager returns different objects from the container for each thread. The behavior is as follows:

  • If you used the RegisterType method to register a type, Unity creates a new instance of the registered type the first time the type is resolved in a specified thread, either to answer a call to the Resolve or ResolveAll methods for the registered type or to fulfill a dependency while resolving a different type. Subsequent resolutions on the same thread return the same instance.
  • Using the RegisterInstance method to register an existing object results in the same behavior as if you just registered the lifetime container with RegisterType. Therefore, it is recommended that you do not use the RegisterInstance method to register an existing object when using the PerThreadLifetimeManager.
  • PerThreadLifetimeManager returns the object desired or permits the container to create a new instance if no such object is currently stored for the current thread. A new instance is also created if called on a different thread than the one that set the value. This lifetime manager does not dispose of the instances it holds.