I have a love hate relationship with Microsoft Unity, the dependency injection container.
Unity is a very powerful tool, it is an extensible industrial strength container that comes equipped with a ton of features right out of box. However my big beef with it is how those features are not always discoverable, and often they are less than intuitive.
For example, let's talk about named registration. You can register a type from the container with or without a name. This means you can then ask the container to Resolve just the type itself, getting back the unnamed registration, or you can ask the container to Resolve that type for a particular name. That is great, it is a feature required to register multiple implementations of the same interface.
The ResolveAll method, however, is only for use with named registrations.
There is no way to resolve a collection of both named and unnamed registrations. That is not a bad thing in and of iteself, but it does mean that if you want to register a "default" type you will need to register it twice. (For more help with that see my previous blog post: Understanding Unity Lifetime Managers)
Equally interesting is how ResolveAll returns you an IEnumerable by reference.
This means that that enumerable you get back will dynamically change with registrations being made against the container. This might sound neat, but it raises a few big questions...
Is it thread safe? Nope!
Is that documented? Nope!
ResolveAll Unit Tests
Here is a detailed unit test to demonstrate how ResolveAll works.
public interface ISomething { }
public class Zero : ISomething { }
public class One : ISomething { }
public class Two : ISomething { }
[Fact]
public void ResolveAll()
{
using (var container = new UnityContainer())
{
// ResolveAll registered types of ISomething,
// this IEnumerable will change with the container.
var things = container.ResolveAll<ISomething>();
Assert.Equal(0, things.Count());
// Register an ISomething without a name, it will
// not be included in the ResolveAll enumerable.
container.RegisterType<ISomething, Zero>();
Assert.Equal(0, things.Count());
// Register an ISomething with a name, this will
// be included in the ResolveAll enumerable.
container.RegisterType<ISomething, One>("One");
Assert.Equal(1, things.Count());
// Register anything to ISomething[], and that will
// resolve a unique collection of registered types
// that is not update with the container.
container.RegisterType<IList<ISomething>, ISomething[]>();
// This first things list will not change when new items
// are added to the container.
var thingsList1 = container.Resolve<IList<ISomething>>();
Assert.Equal(1, thingsList1.Count);
// Register another ISomething with a name, it will
// again be included in the ResolveAll enumerable.
container.RegisterType<ISomething, Two>("Two");
Assert.Equal(2, things.Count());
// This second things list has 2 item, but the first
// things list still only has one.
var thingsList2 = container.Resolve<IList<ISomething>>();
Assert.Equal(2, thingsList2.Count);
Assert.Equal(1, thingsList1.Count);
}
}
Enjoy,
Tom
Thanks Tom!
ReplyDeleteGlad I read this before I started my process instead of trying to figure out WTF was going on after.
Will
This is also a very good post which I really enjoy reading. It is not everyday that I have the possibility to see something like this. 188bet
ReplyDelete