The Funq IoC Container
I was asked to look at Funq, an IoC container that aims to be lightweight and high performance, using lambdas and generics to eliminate the use of runtime reflection. It was made by Daniel Cazzulino (@kzu), and in a series of screen casts he shown the whole process of developing Funq using TDD. This is very impressive and may be a good introduction to TDD, but aside from this, there is little or no documentation. Videos are all very well, but they are no substitute for written code examples. So I may have missed how to do things in Funq.
Also worrying is that the last updated date on the codeplex project is February 2009. Is Funq finished and perfect or just abandoned? I didn't find it to be quite perfect for what I want to do.
The container is mutable and registration works via lambdas. On resolution, you get the same instance each time by default, but the ReusedWithin syntax will result in a new instance.
The last few tests - object with a constructor parameter, the constructed object and the object factory method tests - all look much the same. This shows the simplicity of Funq and the power of using lambdas.
As promised, the syntax is quite simple readable, on a par with the equivalent code in Unity or Autofac. But the conciseness is a bit marred by one thing. Note how constructor arguments are specified for each type. Funq does not do autowiring, but I think that autowiring is a basic characteristic of an IoC container - it allows a class's contructor to be changed at will, without any knock-on effect at all, and the correct services will be supplied automatically via the container. With funq, you must update the classe's registration when the constructor changes.
If you want autowiring with Funq, there is some discussion and code on the topic. However some coding will be required to make this code work. It would be better if this aproach or another was folded into Func. As it is, Func is a good demo, but it doesn't look suitable for general production use.
The test code
namespace IoCComparison
{
using NUnit.Framework;
using Funq;
[TestFixture]
public class FunqTest
{
[Test]
public void CanMakeSweetShopWithVanillaJellybeans()
{
Container container = new Container();
container.Register<IJellybeanDispenser>(c => new VanillaJellybeanDispenser());
container.Register(c => new SweetVendingMachine(c.Resolve<IJellybeanDispenser>()));
container.Register(c => new SweetShop(c.Resolve<SweetVendingMachine>()));
SweetShop sweetShop = container.Resolve<SweetShop>();
Assert.AreEqual(Jellybean.Vanilla, sweetShop.DispenseJellyBean());
}
[Test]
public void CanMakeSweetShopWithStrawberryJellybeans()
{
Container container = new Container();
container.Register<IJellybeanDispenser>(c => new StrawberryJellybeanDispenser());
container.Register(c => new SweetVendingMachine(c.Resolve<IJellybeanDispenser>()));
container.Register(c => new SweetShop(c.Resolve<SweetVendingMachine>()));
SweetShop sweetShop = container.Resolve<SweetShop>();
Assert.AreEqual(Jellybean.Strawberry, sweetShop.DispenseJellyBean());
}
/// <summary>
/// This is not the test that I wanted, but it passes and thus describes funq's behaviour
/// </summary>
[Test]
public void JellybeanDispenserHasSameInstanceEachTime()
{
Container container = new Container();
container.Register<IJellybeanDispenser>(c => new StrawberryJellybeanDispenser());
container.Register(c => new SweetVendingMachine(c.Resolve<IJellybeanDispenser>()));
container.Register(c => new SweetShop(c.Resolve<SweetVendingMachine>()));
SweetShop sweetShop = container.Resolve<SweetShop>();
SweetShop sweetShop2 = container.Resolve<SweetShop>();
Assert.IsTrue(ReferenceEquals(sweetShop, sweetShop2), "Root objects are equal");
Assert.IsTrue(ReferenceEquals(sweetShop.SweetVendingMachine, sweetShop2.SweetVendingMachine), "Contained objects are equal");
Assert.IsTrue(ReferenceEquals(sweetShop.SweetVendingMachine.JellybeanDispenser, sweetShop2.SweetVendingMachine.JellybeanDispenser), "services are equal");
}
[Test]
public void CanMakeSingletonJellybeanDispenser()
{
Container container = new Container();
container.Register<IJellybeanDispenser>(c => new StrawberryJellybeanDispenser());
container.Register(c => new SweetVendingMachine(c.Resolve<IJellybeanDispenser>())).ReusedWithin(ReuseScope.None);
container.Register(c => new SweetShop(c.Resolve<SweetVendingMachine>())).ReusedWithin(ReuseScope.None);
SweetShop sweetShop = container.Resolve<SweetShop>();
SweetShop sweetShop2 = container.Resolve<SweetShop>();
Assert.IsFalse(ReferenceEquals(sweetShop, sweetShop2), "Root objects are equal");
Assert.IsFalse(ReferenceEquals(sweetShop.SweetVendingMachine, sweetShop2.SweetVendingMachine), "Contained objects are equal");
// should be same service
Assert.IsTrue(ReferenceEquals(sweetShop.SweetVendingMachine.JellybeanDispenser, sweetShop2.SweetVendingMachine.JellybeanDispenser), "services are not equal");
}
[Test]
public void CanMakeAniseedRootObject()
{
Container container = new Container();
container.Register<SweetShop>(c => new AniseedSweetShop());
SweetShop sweetShop = container.Resolve<SweetShop>();
Assert.AreEqual(Jellybean.Aniseed, sweetShop.DispenseJellyBean());
}
[Test]
public void CanUseAnyJellybeanDispenser()
{
Container container = new Container();
container.Register<IJellybeanDispenser>(c => new StrawberryJellybeanDispenser());
container.Register(c => new SweetVendingMachine(c.Resolve<IJellybeanDispenser>()));
container.Register(c => new SweetShop(c.Resolve<SweetVendingMachine>()));
container.Register<IJellybeanDispenser>( c => new AnyJellybeanDispenser(Jellybean.Lemon));
SweetShop sweetShop = container.Resolve<SweetShop>();
Assert.AreEqual(Jellybean.Lemon, sweetShop.DispenseJellyBean());
}
[Test]
public void CanUseConstructedObject()
{
Container container = new Container();
container.Register<IJellybeanDispenser>(c => new StrawberryJellybeanDispenser());
container.Register(c => new SweetVendingMachine(c.Resolve<IJellybeanDispenser>()));
container.Register(c => new SweetShop(c.Resolve<SweetVendingMachine>()));
IJellybeanDispenser instance = new AnyJellybeanDispenser(Jellybean.Cocoa);
container.Register(c => instance);
SweetShop sweetShop = container.Resolve<SweetShop>();
Assert.AreEqual(Jellybean.Cocoa, sweetShop.DispenseJellyBean());
}
[Test]
public void CanUseObjectFactory()
{
Container container = new Container();
container.Register<IJellybeanDispenser>(c => new StrawberryJellybeanDispenser());
container.Register(c => new SweetVendingMachine(c.Resolve<IJellybeanDispenser>()));
container.Register(c => new SweetShop(c.Resolve<SweetVendingMachine>()));
container.Register<IJellybeanDispenser>(c => new AnyJellybeanDispenser(Jellybean.Orange));
SweetShop sweetShop = container.Resolve<SweetShop>();
Assert.AreEqual(Jellybean.Orange, sweetShop.DispenseJellyBean());
}
[Test]
public void CanRegisterMultipleDispensers()
{
Assert.Inconclusive("probably can't do this in Func() - " +
"there's no method container.ResolveAll and resolving an IEnumerable<IJellybeanDispenser> doesn't work either");
}
}
}
2 Comments
lusse said
Thanx for the post. We based our decision to skip Funq pretty much on this post due the inactivity of the creator.
AVee said
There is a version of Funq alive and well (and including autowiring) available as a part of the ServiceStack framework.