Extending and Demonstrating the Type Tracking Extension for Unity

Recently, I have received a few requests for some changes and an example for my Type Tracking Extension for Unity. This post is to address those requests. Of course, all of the source code for this post is added to the GitHub project Unity Extensions.

Part One – Extension Methods for the Type Tracking Extension

The number one request I have received, and my own number one issue with using the Type Tracking Extension, is the verbosity of each method call. Here is a typical method call.

container.Configure<TypeTrackingExtension>().TryResolve<ITest>();

This is the typical extension usage model for Unity. Besides the verbosity, it implies that I am configuring the extension, when I am just trying to use it. I try to avoid code that implies something other than what I intend, so this syntax bothers me.

To address this issue, I have added a new class of extension methods to the Type Tracking project. Here is the source code.

namespace hazware.unity.extensions
{
  /// <summary>
  /// Extension methods to simplify using the TypeTrackingExtension for Unity.
  /// Requires .NET 3.5
  /// </summary>
  public static class UnityExtensions
  {
    #region CanResolve
    /// <summary>
    /// Determines whether this type can be resolved as the default.
    /// </summary>
    /// <typeparam name="T">The type to test for resolution</typeparam>
    /// <param name="container">The unity container.</param>
    /// <returns>
    ///     <c>true</c> if this instance can resolve; otherwise, <c>false</c>.
    /// </returns>
    public static bool CanResolve<T>(this IUnityContainer container)
    {
      return container.Configure<TypeTrackingExtension>().CanResolve<T>();
    }
    /// <summary>
    /// Determines whether this type can be resolved with the specified name.
    /// </summary>
    /// <typeparam name="T">The type to test for resolution</typeparam>
    /// <param name="container">The unity container.</param>
    /// <param name="name">The name associated with the type.</param>
    /// <returns>
    ///     <c>true</c> if this instance can resolve; otherwise, <c>false</c>.
    /// </returns>
    public static bool CanResolve<T>(this IUnityContainer container, string name)
    {
      return container.Configure<TypeTrackingExtension>().CanResolve<T>(name);
    }
    /// <summary>
    /// Determines whether this instance can be resolved at all with or without a name.
    /// </summary>
    /// <typeparam name="T">The type to test for resolution</typeparam>
    /// <param name="container">The unity container.</param>
    /// <returns>
    ///     <c>true</c> if this instance can resolve; otherwise, <c>false</c>.
    /// </returns>
    public static bool CanResolveAny<T>(this IUnityContainer container)
    {
      return container.Configure<TypeTrackingExtension>().CanResolveAny<T>();
    }
    #endregion
    
    #region TryResolve
    /// <summary>
    /// Tries to resolve the type, returning null if not found.
    /// </summary>
    /// <typeparam name="T">The type to try and resolve</typeparam>
    /// <param name="container">The unity container.</param>
    /// <param name="container">The unity container.</param>
    /// <returns>An object of type <see cref="T"/> if found, or <c>null</c> if not.</returns>
    /// <param name="container">The unity container.</param>
    public static T TryResolve<T>(this IUnityContainer container)
    {
      return container.Configure<TypeTrackingExtension>().TryResolve<T>();
    }
    /// <summary>
    /// Tries to resolve the type with the specified of name, returning null if not found.
    /// </summary>
    /// <typeparam name="T">The type to try and resolve</typeparam>
    /// <param name="container">The unity container.</param>
    /// <param name="name">The name associated with the type.</param>
    /// <returns>An object of type <see cref="T"/> if found, or <c>null</c> if not.</returns>
    public static T TryResolve<T>(this IUnityContainer container, string name)
    {
      return container.Configure<TypeTrackingExtension>().TryResolve<T>(name);
    }
    /// <summary>
    /// Tries to resolve the type, returning the passed in defaultValue if not found.
    /// </summary>
    /// <typeparam name="T">The type to try and resolve</typeparam>
    /// <param name="container">The unity container.</param>
    /// <param name="defaultValue">The default value.</param>
    /// <returns>An object of type <see cref="T"/> if found, or the <see cref="defaultValue"/> if not.</returns>
    public static T TryResolve<T>(this IUnityContainer container, T defaultValue)
    {
      return container.Configure<TypeTrackingExtension>().TryResolve<T>(defaultValue);
    }
    /// <summary>
    /// Tries to resolve the type, returning the passed in defaultValue if not found.
    /// </summary>
    /// <typeparam name="T">The type to try and resolve</typeparam>
    /// <param name="container">The unity container.</param>
    /// <param name="name">The name associated with the type.</param>
    /// <param name="defaultValue">The default value.</param>
    /// <returns>An object of type <see cref="T"/> if found, or the <see cref="defaultValue"/> if not.</returns>
    public static T TryResolve<T>(this IUnityContainer container, string name, T defaultValue)
    {
      return container.Configure<TypeTrackingExtension>().TryResolve<T>(name, defaultValue);
    }
    #endregion
    
    #region ResolveAll
    /// <summary>
    /// Resolves all elements of a type, including the default.
    /// </summary>
    /// <typeparam name="T">The type to resolve</typeparam>
    /// <param name="container">The unity container.</param>
    /// <returns><see cref="IEnumerable"/> of T</returns>
    public static IEnumerable<T> ResolveAllToEnumerable<T>(this IUnityContainer container)
    {
      return container.Configure<TypeTrackingExtension>().ResolveAll<T>();
    }
    /// <summary>
    /// Resolves all registered T in the container, conditionally including the default unnamed
    /// registered T. When includeDefault is false, this is the same as the normal Unity
    /// ResolveAll.
    /// </summary>
    /// <typeparam name="T">The type to resolve</typeparam>
    /// <param name="container">The unity container.</param>
    /// <param name="includeDefault">if set to <c>true</c> include default value, else do not include default.</param>
    /// <returns><see cref="IEnumerable"/> of T</returns>
    public static IEnumerable<T> ResolveAllToEnumerable<T>(this IUnityContainer container, bool includeDefault)
    {
      return container.Configure<TypeTrackingExtension>().ResolveAll<T>(includeDefault);
    }
    /// <summary>
    /// Resolves all elements of a type, including the default, and returns 
    /// as an array of T.
    /// </summary>
    /// <typeparam name="T">The type to resolve</typeparam>
    /// <param name="container">The unity container.</param>
    /// <returns>Array of T</returns>
    public static T[] ResolveAllToArray<T>(this IUnityContainer container)
    {
      return container.Configure<TypeTrackingExtension>().ResolveAllToArray<T>();
    }
    /// <summary>
    /// Resolves all registered T in the container, conditionally including the default unnamed
    /// registered T. When includeDefault is false, this is the same as the normal Unity
    /// ResolveAll.
    /// </summary>
    /// <typeparam name="T">The type to resolve</typeparam>
    /// <param name="container">The unity container.</param>
    /// <param name="includeDefault">if set to <c>true</c> include default value, else do not include default.</param>
    /// <returns>Array of T</returns>
    public static T[] ResolveAllToArray<T>(this IUnityContainer container, bool includeDefault)
    {
      return container.Configure<TypeTrackingExtension>().ResolveAllToArray<T>(includeDefault);
    }
    #endregion
  }
}

Using these extension methods makes the above sample code look like this:

container.TryResolve<ITest>();

This is more concise, and more importantly, clearly states the meaning of the code. However, the catch for this syntactic sugar is that it uses extension methods. That means that .NET 3.5 is now a requirement for the Type Tracking Extension.

Part Two – Type Tracking Usage Sample{:.float_right}

I have received a few requests for a simple example of how to use the Type Tracking Extension. So here it is.

Step 1 – Add a reference to the hazware.unity.extensions assembly into your project.

Step 2 – Add the following using statement to your source file:

using hazware.unity.extensions;

Step 3 – Register the extension with the Unity container. Here is some sample code:

IUnityContainer container = new UnityContainer();
container.AddNewExtension<TypeTrackingExtension>();

Step 4 – Use the extension. Here some more sample code:

Console.WriteLine("CanResolve<IFoo>() == {0}", container.CanResolve<IFoo>());
Console.WriteLine("CanResolve<IBar>() == {0}", container.CanResolve<IBar>());

That’s it. So here is a complete sample program.

class Program
{
  static void Main(string[] args)
  {
    IUnityContainer container = new UnityContainer();
    container.AddNewExtension<TypeTrackingExtension>();
    
    container.RegisterType<IFoo, AFoo>();
    container.RegisterType<IFoo, AnotherFoo>("Named");
    
    Console.WriteLine("CanResolve<IFoo>() == {0}", container.CanResolve<IFoo>());
    Console.WriteLine("CanResolve<IFoo>(\"Named\") == {0}", container.CanResolve<IFoo>("Named"));
    Console.WriteLine("CanResolve<IBar>() == {0}", container.CanResolve<IBar>());
    
    Console.WriteLine("TryResolve<IFoo>() == null ==> {0}", container.TryResolve<IFoo>() == null);
    Console.WriteLine("TryResolve<IFoo>(\"Named\") == null ==> {0}", container.TryResolve<IFoo>("Named") == null);
    Console.WriteLine("TryResolve<IBar>() == null ==> {0}", container.TryResolve<IBar>() == null);
    
    Console.WriteLine("TryResolve<IBar>(new ABar()) == null ==> {0}", container.TryResolve<IBar>(new ABar()) == null);
    Console.WriteLine("TryResolve<IBar>(\"Named\", new ABar()) == null ==> {0}", container.TryResolve<IBar>("Named", new ABar()) == null);
    
    Console.WriteLine("ResolveAllToEnumerable<IFoo>().Count() == {0}", container.ResolveAllToEnumerable<IFoo>().Count());
    Console.WriteLine("ResolveAllToEnumerable<IFoo>(false).Count() == {0}", container.ResolveAllToEnumerable<IFoo>(false).Count());
    Console.WriteLine("ResolveAllToEnumerable<IBar>().Count() == {0}", container.ResolveAllToEnumerable<IBar>().Count());
    
    Console.WriteLine("ResolveAllToArray<IFoo>().Length == {0}", container.ResolveAllToArray<IFoo>().Length);
    Console.WriteLine("ResolveAllToArray<IFoo>(false).Length == {0}", container.ResolveAllToArray<IFoo>(false).Length);
    Console.WriteLine("ResolveAllToArray<IBar>().Length == {0}", container.ResolveAllToArray<IBar>().Length);
  }
}

This source is in the SampleApp project checked into the Unity Extensions project up on GitHub.

Hopefully, the extension methods make using the Type Tracking Extension easier to use, and the sample helps to demonstrates its usage.