Type Tracking Extension for Unity

February 8th, 2009 | Categories: Programming | Tags: , ,

Edit: Source code posted to Hazware Unity Extensions on GitHub

My personal inversion of control container is Unity. However, I occasionally find myself looking for functionality from the other .NET IoC containers I have used, such as Ninject and Castle Windsor. Here is the code that came out from trying to solve this need.

I was working on some Unit of Work code for NHibernate and I found the need to query the container to check if a type was registered. I checked the Unity documentation for the IUnityContainer.Resolve<>() method, and as usual, the documentation always seems to be missing the piece of information I need. It did not describe the result of calling the container for a type that is not registered. Having used Unity for a while now, I was pretty sure it through an exception, but just be sure, I wrote a quick test.

[Test]
public void TestUnity()
{
  IoC.Container.Resolve<IDisposable>();
}


And result:

Microsoft.Practices.Unity.ResolutionFailedException: Resolution of the dependency failed, type = "System.IDisposable", name = "". Exception message is: The current build operation (build key Build Key[System.IDisposable, null]) failed: The current type, System.IDisposable, is an interface and cannot be constructed. Are you missing a type mapping?

I could not find another way to check for type registration, so off to Google I went. One discussion from the Unity discussion group seemed to cover this program [see: IsInstanceRegistered?]. This seemed to be the answer, until I compared against my unit tests to validate my usage. The solution provided does not provide for checked for default or named registered types. The problem is that Unity has a keyed resolver in that it either returns a default registration that is unnamed, or a specifically named registration. Thus, it is possible for this check to return true, but for the Resolve call to fail. I wanted something more in line with Unity’s usage model.

I did find one other posted solution. That solution recommend just doing a ResolveAll and returning true if the collection was not empty. However, this is highly inefficient. It forces resolution of the types, triggering instantiation, even if not needed.

Given these limitations, I decided to write my own solution. I like the idea of doing it as a Unity extension, so I retained that model. So here is my code for solution.

public class TypeTrackingExtension : UnityContainerExtension
{
  #region Fields
  private readonly Dictionary<Type, HashSet<string>> _registeredTypes = new Dictionary<Type, HashSet<string>>();
  #endregion

  #region Private Methods
  protected override void Initialize()
  {
    Context.RegisteringInstance += OnNewInstance;
    Context.Registering += OnNewType;
  }
  private void OnNewInstance(object sender, RegisterInstanceEventArgs e)
  {
    HashSet<string> names;
    string name = string.IsNullOrEmpty(e.Name) ? string.Empty : e.Name;

    if (!_registeredTypes.TryGetValue(e.RegisteredType, out names))
    { //  not found, so add it
      _registeredTypes.Add(e.RegisteredType, new HashSet<string> { name });
    }
    else
    { //  already added type, so add name
      names.Add(name);
    }
  }
  private void OnNewType(object sender, RegisterEventArgs e)
  {
    HashSet<string> names;
    string name = string.IsNullOrEmpty(e.Name) ? string.Empty : e.Name;
    if (!_registeredTypes.TryGetValue(e.TypeFrom, out names))
    { //  not found, so add it
      _registeredTypes.Add(e.TypeFrom, new HashSet<string> { name });
    }
    else
    { //  already added type, so add name
      names.Add(name);
    }
  }
  #endregion

  #region CanResolve
  /// <summary>
  /// Determines whether this type can be resolved as the default.
  /// </summary>
  /// <typeparam name="T"></typeparam>
  /// <returns>
  ///     <c>true</c> if this instance can resolve; otherwise, <c>false</c>.
  /// </returns>
  public bool CanResolve<T>()
  {
    return CanResolve<T>(null);
  }
  /// <summary>
  /// Determines whether this type can be resolved with the specified name.
  /// </summary>
  /// <typeparam name="T"></typeparam>
  /// <param name="name">The name.</param>
  /// <returns>
  ///     <c>true</c> if this instance can be resolved with the specified name; otherwise, <c>false</c>.
  /// </returns>
  public bool CanResolve<T>(string name)
  {
    HashSet<string> names;
    if (_registeredTypes.TryGetValue(typeof(T), out names))
    {
      return names.Contains(name ?? string.Empty);
    }
    return false;
  }
  /// <summary>
  /// Determines whether this instance can be resolved at all.
  /// </summary>
  /// <typeparam name="T"></typeparam>
  /// <returns>
  ///     <c>true</c> if this instance can be resolved at all; otherwise, <c>false</c>.
  /// </returns>
  public bool CanResolveAny<T>()
  {
    return _registeredTypes.ContainsKey(typeof(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>
  /// <returns>An object of type <see cref="T"/> if found, or <c>null</c> if not.</returns>
  public T TryResolve<T>()
  {
    return TryResolve<T>(default(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="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 T TryResolve<T>(string name)
  {
    return TryResolve<T>(name, default(T));
  }
  /// <summary>
  /// Tries to resolve the type, returning null if not found.
  /// </summary>
  /// <typeparam name="T">The type to try and resolve.</typeparam>
  /// <param name="defaultValue">The default value to return if type not found.</param>
  /// <returns>An object of type <see cref="T"/> if found, or the <see cref="defaultValue"/> if not.</returns>
  public T TryResolve<T>(T defaultValue)
  {
    if (!CanResolve<T>())
      return defaultValue;
    return Container.Resolve<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="name">The name associated with the type.</param>
  /// <param name="defaultValue">The default value to return if type not found.</param>
  /// <returns>An object of type <see cref="T"/> if found, or the <see cref="defaultValue"/> if not.</returns>
  public T TryResolve<T>(string name, T defaultValue)
  {
    if (!CanResolve<T>(name))
      return defaultValue;
    return Container.Resolve<T>(name);
  }
  #endregion
}


Kick It on DotNetKicks.com
  1. espinete
    August 20th, 2009 at 08:13
    Reply | Quote | #1

    Hi,

    how I use TypeTrackingExtension ? sample code,please !!!

    thanks