Feeds:
Posts
Comments

Archive for the ‘LucidIoC’ Category

To refresh our memory from part 1 of this post, here’s the test for the named instance functionality we want to implement in the IoC container.

[TestFixture]
public class When_asked_to_get_a_named_object_instance
{
	[Test]
	public void Given_a_name_that_has_been_configured()
	{
		Test.Static()
			.When(asked_to_get_a_named_instance)
			.With(a_name_that_has_been_configured)
			.Should(get_a_non_null_instance)
			.Should(get_the_instance_requested)
			.Should(get_a_different_instance_every_time)
			.Verify();
	}
}

However, before we can get a named instance we have to build out the ability to configure a named instance.

[TestFixture]
public class When_asked_to_update_the_resolution_info
{
	[Test]
	public void Given_a_request_to_name_it()
	{
		Test.Static()
			.When(asked_to_name_it)
			.With(context_containing_the_resolution_info)
			.Should(configure_the_name_of_the_resolution_info)
			.Should(return_the_context)
			.Verify();
	}
}

with test details

	private void asked_to_name_it()
	{
		_name = "Foo";
		_result = _context.Named(_name);
	}

	private void configure_the_name_of_the_resolution_info()
	{
		_resolutionInfo.Name.ShouldBeEqualTo(_name);
	}

which implies

public class ResolutionContext
{
	public ResolutionContext Named(string name)
	{
		return this;
	}

and

public class ResolutionInfo
{
	public string Name { get; set; }

Run the test and get red.

STEP 1: context containing the resolution info
WHEN asked to name it
SHOULD configure the name of the resolution info - FAILED

fix it

	public ResolutionContext Named(string name)
	{
		_resolutionInfo.Name = name;
		return this;
	}

green. Now to build out named instance support in ConfigurationCollection.

[TestFixture]
public class When_asked_to_get_a_named_configuration
{
	[Test]
	public void Given_a_matching_named_configuration_exists()
	{
		Test.Given(_collection)
			.When(asked_to_get_a_named_configuration)
			.With(a_specific_name)
			.With(an_existing_configuration_with_the_requested_name)
			.Should(get_the_requested_configuration)
			.Verify();
	}
}

with test details

	[SetUp]
	public void BeforeEachTest()
	{
		_collection = new ConfigurationCollection();
	}

	private void a_specific_name()
	{
		_name = "Foo";
	}

	private void an_existing_configuration_with_the_requested_name()
	{
		_configuration = new ResolutionInfo();
		new ResolutionContext(_configuration).Named(_name);
		_collection.Store(_configuration);
	}

	private void asked_to_get_a_named_configuration()
	{
		_result = _collection.Get(_name);
	}

	private void get_the_requested_configuration()
	{
		_result.ShouldBeSameInstanceAs(_configuration);
	}

which implies an overload for Get()

public class ConfigurationCollection
{
	public ResolutionInfo Get(string name)
	{
		return null;
	}

Run the test and get red.

STEP 1: a specific name
STEP 2: an existing configuration with the requested name
WHEN asked to get a named configuration
SHOULD get the requested configuration - FAILED

fix it.

	public ResolutionInfo Get(string name)
	{
		return _configurations.Single(x=>x.Name == name);
	}

green

Now, does the collection support two differently named configurations at the same time?

public class When_asked_to_store_a_configuration
{
	[Test]
	public void Given_a_named_configuration_and_a_differently_named_configuration_already_exists()
	{
		Test.Given(_collection)
			.When(asked_to_store_a_configuration)
			.With(an_existing_named_configuration)
			.With(a_named_configuration)
			.Should(add_the_new_named_configuration)
			.Should(not_remove_the_other_named_configuration)
			.Verify();
	}

	private void an_existing_unnamed_configuration()
	{
		_existingConfiguration = new ResolutionInfo
			{
				IsSingleton = true
			};
		_collection.Store(_existingConfiguration);
	}

	private void a_named_configuration()
	{
		_newConfiguration = new ResolutionInfo
			{
				Name = "Bar"
			};
	}

	private void add_the_new_named_configuration()
	{
		_collection.Get(_newConfiguration.Name)
			.ShouldBeSameInstanceAs(_newConfiguration);
	}

	private void not_remove_the_other_named_configuration()
	{
		_collection.Get(_existingConfiguration.Name)
			.ShouldBeSameInstanceAs(_existingConfiguration);
	}

red

STEP 1: an existing named configuration
STEP 2: a named configuration
WHEN asked to store a configuration
SHOULD add the new named configuration - PASSED
SHOULD not remove the other named configuration - FAILED

fix it

public class ConfigurationCollection
{
	public void Store(ResolutionInfo configuration)
	{
		var match = _configurations.FirstOrDefault(x => x.Name == configuration.Name);
		if (match == null)
		{
			_configurations.Add(configuration);
		}
		else
		{
			int index = _configurations.IndexOf(match);
			_configurations[index] = configuration;
		}
	}

green.

A subtle problem here is the existing configuration may already have an instance. So we need to make sure its gets disposed.

	[Test]
	public void Given_a_configuration_and_a_configuration_with_a_disposable_instance_already_exists()
	{
		Test.Given(_collection)
			.When(asked_to_store_a_configuration)
			.With(an_existing_configuration)
			.With(an_existing_disposable_instance)
			.With(a_new_configuration)
			.Should(dispose_of_the_existing_instance)
			.Verify();
	}

	private void an_existing_disposable_instance()
	{
		_instance = new DisposeTester();
		_existingConfiguration.Instance = _instance;
	}

	private void dispose_of_the_existing_instance()
	{
		_instance.Disposed.ShouldBeTrue();
	}

red

STEP 1: an existing configuration
STEP 2: an existing disposable instance
STEP 3: a new configuration
WHEN asked to store a configuration
SHOULD dispose of the existing instance - FAILED

fix it by refactoring an instance handling method out of DisposeDisposableInstances() and calling it

	public void DisposeDisposableInstances()
	{
		foreach (var configuration in _configurations)
		{
			DisposeInstance(configuration);
		}
	}

	private static void DisposeInstance(ResolutionInfo configuration)
	{
		var instance = configuration.Instance as IDisposable;
		configuration.Instance = null;
		if (instance == null)
		{
			return;
		}
		instance.Dispose();
	}

	public void Store(ResolutionInfo configuration)
	{
		var match = _configurations.FirstOrDefault(x => x.Name == configuration.Name);
		if (match == null)
		{
			_configurations.Add(configuration);
		}
		else
		{
			int index = _configurations.IndexOf(match);
			_configurations[index] = configuration;
			DisposeInstance(match);
		}
	}

run the test again and get green.

Now let’s spiral back to Get() being called when we expect only one configuration but there are multiple. This wasn’t a possibility before but now it is. First some refactoring to the method names in the existing test.

[TestFixture]
public class When_asked_to_get_the_configuration
{
	[Test]
	public void Given_a_single_configuration_exists()
	{
		Test.Given(_collection)
			.When(asked_to_get_the_configuration)
			.With(an_existing_configuration)
			.Should(get_the_requested_configuration)
			.Verify();
	}

Next the new test.

	[Test]
	public void Given_multiple_configurations_exist()
	{
		Test.Given(_collection)
			.When(asked_to_get_the_configuration)
			.With(an_existing_configuration)
			.With(an_existing_configuration)
			.ShouldThrowException<InvalidOperationException>()
			.Verify();
	}

	private void an_existing_configuration()
	{
		_configuration = new ResolutionInfo
			{
				Name = Guid.NewGuid().ToString()
			};
		_collection.Store(_configuration);
	}

green.

Finally, we can add the ability to get a named instance from LeafIoC.

[TestFixture]
public class When_asked_to_get_a_named_object_instance
{
	[Test]
	public void Given_a_name_that_has_been_configured()
	{
		Test.Static()
			.When(asked_to_get_a_named_instance)
			.With(a_name_that_has_been_configured)
			.Should(get_a_non_null_instance)
			.Should(get_the_instance_requested)
			.Should(get_a_different_instance_every_time)
			.Verify();
	}

	private void asked_to_get_a_named_instance()
	{
		_result = _getInstance();	
	}

	private void a_name_that_has_been_configured()
	{
		LeafIoC.Configure<IComparable, Int32>().Named("Int");
		_getInstance = () => LeafIoC.GetInstance<IComparable>("Int");
	}

	private void get_a_non_null_instance()
	{
		_result.ShouldNotBeNull();
	}

	private void get_the_instance_requested()
	{
		_result.GetType().ShouldBeEqualTo(typeof(int));
	}

	private void get_a_different_instance_every_time()
	{
		_result.ShouldNotBeNull();
		var other = _getInstance();
		_result.ShouldNotBeSameInstanceAs(other);
	}

which implies

public static class LeafIoC
{
	public static TInterface GetInstance<TInterface>(string name)
	{
		return default(TInterface);
	}

red

STEP 1: a name that has been configured
WHEN asked to get a named instance
SHOULD get a non null instance - FAILED

fix it by making the overload with the name the primary and having the overload without the name call it.

	public static TInterface GetInstance<TInterface>()
	{
		return GetInstance<TInterface>(null);
	}

	public static TInterface GetInstance<TInterface>(string name)
	{ 
		...

green but the name wasn’t used. revise the test.

	[Test]
	public void Given_a_name_that_has_been_configured()
	{
		Test.Static()
			.When(asked_to_get_a_named_instance)
			.With(a_name_that_has_been_configured)
			.With(another_name_has_been_configured)
			.Should(get_a_non_null_instance)
			.Should(get_the_instance_requested)
			.Should(get_a_different_instance_every_time)
			.Verify();
	}

	private static void another_name_has_been_configured()
	{
		LeafIoC.Configure<IComparable, Decimal>().Named("Decimal");
	}

red

STEP 1: a name that has been configured
STEP 2: another name has been configured
WHEN asked to get a named instance - FAILED

But this is not the failure we were expecting. What happened? The only thing we did was call LeafIoC.Configure twice so lets take a look at that method.

	public static ResolutionContext Configure<TInterface, TImplementation>() where TImplementation : TInterface, new()
	{
		var type = typeof(TInterface);
		var configuration = new ConfigurationCollection();
		var resolutionInfo = new ResolutionInfo
			{
				Initializer = (Func<TInterface>)(() => new TImplementation())
			};
		configuration.Store(resolutionInfo);
		Configuration[type] = configuration;

		return new ResolutionContext(resolutionInfo);
	}

Oh ho, the problem is the method creates a new ConfigurationCollection every time instead of reusing the existing one. That was the correct behavior when we only had one configuration per interface but now ConfigurationCollection supports multiple configurations so this method needs to be updated. First refactor our existing test to detect the problem:

[TestFixture]
public class When_asked_to_configure_a_type
{
	[Test]
	public void Given_a_type_that_has_already_been_configured()
	{
		Test.Static()
			.When(asked_to_configure_a_type)
			.With(a_type_that_has_already_been_configured)
			.Should(store_the_requested_configuration)
			.Should(still_contain_the_other_configuration)
			.Verify();
	}

	private void a_type_that_has_already_been_configured()
	{
		LeafIoC.Configure<IComparable, decimal>().Named("Decimal");
	}

	private void a_type_that_has_not_been_configured()
	{
		LeafIoC.Reset();
	}

	private void asked_to_configure_a_type()
	{
		LeafIoC.Configure<IComparable, Int32>();
		_expectedType = typeof(Int32);
	}

	private void still_contain_the_other_configuration()
	{
		var result = LeafIoC.GetInstance<IComparable>("Decimal");
		result.GetType().ShouldBeEqualTo(typeof(decimal));
	}

	private void store_the_requested_configuration()
	{
		var result = LeafIoC.GetInstance<IComparable>();
		result.GetType().ShouldBeEqualTo(_expectedType);
	}

red

STEP 1: a type that has already been configured
WHEN asked to configure a type
SHOULD store the requested configuration - PASSED
SHOULD still contain the other configuration - FAILED

fix it

	public static ResolutionContext Configure<TInterface, TImplementation>() where TImplementation : TInterface, new()
	{
		var type = typeof(TInterface);
		ConfigurationCollection configuration;
		if (!Configuration.TryGetValue(type, out configuration))
		{
			configuration = new ConfigurationCollection();
		}

try the test again and red?!

STEP 1: a type that has already been configured
WHEN asked to configure a type
SHOULD store the requested configuration - FAILED

System.InvalidOperationException : Sequence contains more than one element
at System.Linq.Enumerable.Single(IEnumerable`1 source)
at gar3t.LucidIoC.ConfigurationCollection.Get() in ConfigurationCollection.cs: line 32

Yep. That makes sense. Remember we added an overload for Get() but we didn’t update the original method to behave consistently when some objects are named and some are not. So push what we’re doing onto the stack and go fix that. First some refactoring.

public class When_asked_to_get_the_configuration
{
	[Test]
	public void Given_a_single_named_configuration_exists()
	{
		Test.Given(_collection)
			.When(asked_to_get_the_configuration)
			.With(an_existing_named_configuration)
			.Should(get_the_named_configuration)
			.Verify();
	}

	[Test]
	public void Given_multiple_named_configurations_exist()
	{
		Test.Given(_collection)
			.When(asked_to_get_the_configuration)
			.With(an_existing_named_configuration)
			.With(an_existing_named_configuration)
			.ShouldThrowException<InvalidOperationException>()
			.Verify();
	}

	private void an_existing_named_configuration()
	{
		_namedConfiguration = new ResolutionInfo
			{
				Name = Guid.NewGuid().ToString()
			};
		_collection.Store(_namedConfiguration);
	}

	private void get_the_named_configuration()
	{
		_result.ShouldBeSameInstanceAs(_namedConfiguration);
	}

now add a test for single unnamed

	[Test]
	public void Given_a_single_unnamed_configuration_exists()
	{
		Test.Given(_collection)
			.When(asked_to_get_the_configuration)
			.With(an_existing_unnamed_configuration)
			.Should(get_the_unnamed_configuration)
			.Verify();
	}

	private void an_existing_unnamed_configuration()
	{
		_unnamedConfiguration = new ResolutionInfo();
		_collection.Store(_unnamedConfiguration);
	}

	private void get_the_unnamed_configuration()
	{
		_result.ShouldBeSameInstanceAs(_unnamedConfiguration);
	}

green. Next a test for multiple unnamed configurations. In this case by design the second replaces the first when stored.

	[Test]
	public void Given_multiple_unnamed_configurations_were_stored()
	{
		Test.Given(_collection)
			.When(asked_to_get_the_configuration)
			.With(an_existing_unnamed_configuration)
			.With(an_existing_unnamed_configuration)
			.Should(get_the_last_stored_unnamed_configuration)
			.Verify();
	}

	private void get_the_last_stored_unnamed_configuration()
	{
		get_the_unnamed_configuration();
	}

And finally the problem case:

	[Test]
	public void Given_multiple_configurations_exist_but_one_is_unnamed()
	{
		Test.Given(_collection)
			.When(asked_to_get_the_configuration)
			.With(an_existing_named_configuration)
			.With(an_existing_unnamed_configuration)
			.Should(get_the_unnamed_configuration)
			.Verify();
	}

red

STEP 1: an existing named configuration
STEP 2: an existing unnamed configuration
WHEN asked to get the configuration - FAILED

System.InvalidOperationException : Sequence contains more than one element

fix it

	public ResolutionInfo Get()
	{
		return _configurations.SingleOrDefault(x=>x.Name == null) ?? _configurations.Single();
	}

green.
Now pop up the stack to the problem we were fixing in LeafIoC.Configure() and re-run the tests. red again.

STEP 1: a type that has already been configured
WHEN asked to configure a type
SHOULD store the requested configuration - PASSED
SHOULD still contain the other configuration - FAILED

The problem here is we’re using the method LeafIoC.GetInstance(name) in our verification and that method is still red. There isn’t another option on the public interface to use in the test so switch back to the test for LeafIoC.Get(name) and re-run the test. red.

STEP 1: a name that has been configured
STEP 2: another name has been configured
WHEN asked to get a named instance - FAILED

System.InvalidOperationException : Sequence contains more than one element
at System.Linq.Enumerable.Single(IEnumerable`1 source)
at gar3t.LucidIoC.ConfigurationCollection.Get() in ConfigurationCollection.cs: line 32

now fix it.

	public static TInterface GetInstance<TInterface>(string name)
	{
...
		var info = name != null ? configuration.Get(name) : configuration.Get();
...

green. Now re-run the LeafIoC.Configure() that was breaking a minute ago. green.

We’ve accomplished the goal of implementing support for named instances in the IoC container.

About halfway through this post I came to the realization that I really wanted to use the word Configuration instead of ResolutionInfo but I thought it might be confusing to rename everything in the middle so I waited. The code for this post has been checked in and now I’ll do some renaming and refactoring to cleanup offline.

As always, I’ll announce the availability of the next post in this series of posts on twitter.

The code for this project is available from github.

Read Full Post »

In the second post of this series we modified LeafIoC to support singleton instances. This time we’ll implement support for named instances. Named instances are useful when you have different implementations of an interface and you want the ability to get a specific implementation in one case and a different one in another case. For example, lets say a Car has an IEngine dependency. If you are building a sports car you may want an instance of HighPerformanceEngine. If you are building a hybrid car you may prefer an instance of FuelEfficientEngine. Our IoC container doesn’t support dependency injection just yet but when it does we’ll want the ability to configure the system such that a SportsCar gets a HighPerformanceEngine and a Hybrid gets a FuelEfficientEngine. One way to do that is with named instances.

Here’s a test describing the functionality we’re going to implement:

[TestFixture]
public class When_asked_to_get_a_named_object_instance
{
	[Test]
	public void Given_a_name_that_has_been_configured()
	{
		Test.Static()
			.When(asked_to_get_a_named_instance)
			.With(a_name_that_has_been_configured)
			.Should(get_a_non_null_instance)
			.Should(get_the_instance_requested)
			.Should(get_a_different_instance_every_time)
			.Verify();
	}
}

Note that we expect to get a different instance every time we request the named instance. A named instance isn’t automatically a singleton. The name simply represents a particular implementation or possibly a specific configuration of an object, not a particular instance.

As before let’s start by thinking about the API implications of this functionality. We’ll want to specify the name in a manner similar to the way we specify singleton, e.g.

	LeafIoC.Configure<IEngine, HighPerformanceEngine>()
		.AsSingleton()
		.Named("HP");

And order shouldn’t matter

	LeafIoC.Configure<IEngine, HighPerformanceEngine>()
		.Named("HP")
		.AsSingleton();

We’ll want the ability to have multiple named instances, but only one with a particular name is valid. This implies some error handling around missing named instances and asking for an unnamed instance when we only have named ones.

As for requesting an instance, we could add a new method like:

	LeafIoC.GetNamedInstance<IEngine>("HP");

But given the name we can infer that a named instance is being requested, so maybe:

	LeafIoC.GetInstance<IEngine>("HP");

Now consider that down the line we may want the ability to get instances based on features other than name. If we do that I’d lean towards passing a lambda like:

	LeafIoC.GetInstance<IEngine>(x=>x.CachePolicy == HttpContext);

That may also be a YAGNI but it doesn’t hurt to choose an implementation that isn’t far off in case we end up there so we’ll go with passing the name as a parameter to GetInstance() for now.

To support multiple named instances we’re going to have to change the internal Configuration Dictionary to store a collection instead of a single ResolutionInfo. What collection should we use? IList works but if we end up with a bunch of instances it will be inefficient (O(n/2) on average) to find a particular instance. If we go with an IDictionary we have the problem that we don’t have the name until after we’ve stored the ResolutionInfo under the unnamed key. What to do? The vast majority of configurations will likely be unnamed so the potential inefficiency of searching a list is outweighed by the simplified housekeeping around named instances. Let’s go with the IList. The next thing to consider is, the semantics of getting objects from a List inside a Dictionary can make for really impenetrable code. So I’m going to opt for a container around a list that exposes methods like Get(string name) to abstract away the complexity. Here’s the stub class.

public class ConfigurationCollection
{
	private readonly IList<ResolutionInfo> _configurations
		= new List<ResolutionInfo>();
}

Now we’re going to mirror some of the functionality from LeafIoC in ConfigurationCollection and just like the previous post we’ll start with tests around the simplest feature and bootstrap our way up.

[TestFixture]
public class When_asked_if_a_configuration_exists
{
	[Test]
	public void Given_nothing_has_been_configured()
	{
		Test.Given(_collection)
			.When(asked_if_a_configuration_exists)
			.With(nothing_configured)
			.Should(return_false)
			.Verify();
	}
}

with test details

	[SetUp]
	public void BeforeEachTest()
	{
		_collection = new ConfigurationCollection();
	}

	private void asked_if_a_configuration_exists()
	{
		_result = _collection.HasConfiguration();
	}

	private static void nothing_configured()
	{
	}

	private void return_false()
	{
		_result.ShouldBeFalse();
	}

which implies

public class ConfigurationCollection
{
	public bool HasConfiguration()
	{
		return true;
	}

run the test and get red

STEP 1: nothing configured
WHEN asked if an instance is configured
SHOULD return false - FAILED

fix it

	public bool HasConfiguration()
	{
		return _configurations.Count != 0;
	}

green. Next to drive out more functionality.

[TestFixture]
public class When_asked_to_store_a_configuration
{
	[Test]
	public void Given_a_configuration()
	{
		Test.Given(_collection)
			.When(asked_to_store_a_configuration)
			.With(a_new_configuration)
			.Should(store_the_configuration)
			.Verify();
	}
}

with test details

	[SetUp]
	public void BeforeEachTest()
	{
		_collection = new ConfigurationCollection();
	}

	private void a_new_configuration()
	{
		_configuration = new ResolutionInfo();
	}

	private void asked_to_store_a_configuration()
	{
		_collection.Store(_configuration);
	}

	private void store_the_configuration()
	{
		_collection.HasConfiguration().ShouldBeTrue();
	}

which implies

public class ConfigurationCollection
{
	public void Store(ResolutionInfo configuration)
	{
		_configurations.Add(configuration);
	}

Run the test and get green. Now we can cycle back to HasConfiguration to make sure we get the correct result if there is something in the collection.

	[Test]
	public void Given_something_has_been_configured()
	{
		Test.Given(_collection)
			.When(asked_if_a_configuration_exists)
			.With(something_configured)
			.Should(return_true)
			.Verify();
	}

details

	private void something_configured()
	{
		_collection.Store(new ResolutionInfo());
	}

	private void return_true()
	{
		_result.ShouldBeTrue();
	}

Run the test and get green.

Next we need the ability to fetch a configuration.

[TestFixture]
public class When_asked_to_get_the_configuration
{
	[Test]
	public void Given_a_configuration_exists()
	{
		Test.Given(_collection)
			.When(asked_to_get_the_configuration)
			.With(an_existing_configuration)
			.Should(get_the_requested_configuration)
			.Verify();
	}
}

test details

	[SetUp]
	public void BeforeEachTest()
	{
		_collection = new ConfigurationCollection();
	}

	private void an_existing_configuration()
	{
		_configuration = new ResolutionInfo();
		_collection.Store(_configuration);
	}

	private void asked_to_get_a_configuration()
	{
		_result = _collection.Get();
	}

	private void get_the_requested_configuration()
	{
		ReferenceEquals(_result, _configuration).ShouldBeTrue();
	}

implies

public class ConfigurationCollection
{
	public ResolutionInfo Get()
	{
		return null;
	}

red

STEP 1: an existing configuration
WHEN asked to get a configuration
SHOULD get the requested configuration - FAILED

fix it

	public ResolutionInfo Get()
	{
		return _configurations.Single();
	}

At this point we’ll could extend this method to throw when a configuration is requested and none exists but if the LeafIoC public API is being used that isn’t possible yet so for now that check is a YAGNI.

Next we need to handle storage of a duplicate configuration. In the existing implementation we replace the existing configuration with the new one. Here’s the test:

	[Test]
	public void Given_a_configuration_and_a_configuration_already_exists()
	{
		Test.Given(_collection)
			.When(asked_to_store_a_configuration)
			.With(an_existing_configuration)
			.With(a_new_configuration)
			.Should(replace_the_existing_configuration)
			.Verify();
	}

	private void an_existing_configuration()
	{
		_collection.Store(new ResolutionInfo
			{
				IsSingleton = true
			});
	}

	private void replace_the_existing_configuration()
	{
		_collection.Get().IsSingleton.ShouldBeFalse();
	}

red

STEP 1: an existing configuration
STEP 2: a new configuration
WHEN asked to store a configuration
SHOULD replace the existing configuration - FAILED

To match existing functionality in LeafIoC we’ll replace the existing configuration.

	public void Store(ResolutionInfo configuration)
	{
		_configurations.Clear();
		_configurations.Add(configuration);
	}

Now we can change the configuration dictionary in LeafIoC to store a ConfigurationCollection instead of a ResolutionInfo. Since we already have tests around LeafIoC we can simply make the change.

	private static readonly Dictionary<Type, ConfigurationCollection> Configuration = new Dictionary<Type, ConfigurationCollection>();

To make it compile we have to update Configure

	public static ResolutionContext Configure<TInterface, TImplementation>() where TImplementation : TInterface, new()
	{
		var type = typeof(TInterface);
		var configuration = new ConfigurationCollection();
		var resolutionInfo = new ResolutionInfo
			{
				Initializer = (Func<TInterface>)(() => new TImplementation())
			};
		configuration.Store(resolutionInfo);
		Configuration[type] = configuration;

		return new ResolutionContext(resolutionInfo);
	}

and GetInstance

	public static TInterface GetInstance<TInterface>()
	{
		var type = typeof(TInterface);
		ConfigurationCollection configuration;
		if (!Configuration.TryGetValue(type, out configuration))
		{
			throw new ConfigurationErrorsException(String.Format("No instance of {0} has been configured.", type.FullName));
		}

		var info = configuration.Get();
		if (info.Instance != null)
		{
			return (TInterface)info.Instance;
		}

		var instance = ((Func<TInterface>)(info.Initializer)).Invoke();
		if (info.IsSingleton)
		{
			info.Instance = instance;
		}
		return instance;
	}

We’ll also have to migrate DisposeDisposableInstances() to ConfigurationCollection. We’ll start by commenting out the contents of the current method and stubbing the new one.

public class ConfigurationCollection
{
	public void DisposeDisposableInstances()
	{
	}

Then we’ll add tests to drive out the functionality we want.

[TestFixture]
public class When_asked_to_dispose_disposable_instances
{
	[Test]
	public void Given_instances_of_disposable_types_exist()
	{
		Test.Given(_collection)
			.When(asked_to_dispose_disposable_instances)
			.With(existing_instances_of_disposable_types)
			.Should(dispose_of_any_disposable_instances)
			.Should(set_instances_to_null)
			.Verify();
	}
}

First move DisposeTester out of LeafIoCTests.When_asked_to_reset so we can access it directly. Now the test details.

	[SetUp]
	public void BeforeEachTest()
	{
		_collection = new ConfigurationCollection();
	}

	private void asked_to_dispose_disposable_instances()
	{
		_collection.DisposeDisposableInstances();
	}

	private void dispose_of_any_disposable_instances()
	{
		_disposeTester.Disposed.ShouldBeTrue();
	}

	private void existing_instances_of_disposable_types()
	{
		_disposeTester = new DisposeTester();
		_collection.Store(new ResolutionInfo
			{
				Instance = _disposeTester
			});
	}

	private void set_instances_to_null()
	{
		_collection.Get().Instance.ShouldBeNull();
	}

red

STEP 1: existing instances of disposable types
WHEN asked to dispose disposable instances
SHOULD dispose of any disposable instances - FAILED

fix it

	public void DisposeDisposableInstances()
	{
		foreach (var configuration in _configurations)
		{
			var instance = (IDisposable)configuration.Instance;
			instance.Dispose();
		}
	}

re-run the test and get red again

STEP 1: existing instances of disposable types
WHEN asked to dispose disposable instances
SHOULD dispose of any disposable instances - PASSED
SHOULD set instances to null - FAILED

fix it

	public void DisposeDisposableInstances()
	{
		foreach (var configuration in _configurations)
		{
			var instance = (IDisposable)configuration.Instance;
			configuration.Instance = null;
			instance.Dispose();
		}
	}

Next make sure the method does the right thing for non-disposable instances.

	[Test]
	public void Given_instances_of_non_disposable_types_exist()
	{
		Test.Given(_collection)
			.When(asked_to_dispose_disposable_instances)
			.With(existing_instances_of_non_disposable_types)
			.Should(set_instances_to_null)
			.Verify();
	}

As with DisposeTester we’ll move NonDisposableTester out of LeafIoCTests.When_asked_to_reset so we can access it directly. Then fill in the test details.

	private void existing_instances_of_non_disposable_types()
	{
		_collection.Store(new ResolutionInfo
			{
				Instance = new NonDisposableTester()
			});
	}

run the test and get red

STEP 1: existing instances of non disposable types
WHEN asked to dispose disposable instances - FAILED

System.InvalidCastException : Unable to cast object of type 'gar3t.LucidIoC.Tests.NonDisposableTester' to type 'System.IDisposable'.

fix it

	public void DisposeDisposableInstances()
	{
		foreach (var configuration in _configurations)
		{
			var instance = configuration.Instance as IDisposable;
			configuration.Instance = null;
			if (instance == null)
			{
				continue;
			}
			instance.Dispose();
		}
	}

Now we can address the failing tests that resulted from commenting out the contents of DisposeDisposeableInstances() in LeafIoC

When_asked_to_reset.Given_instances_of_disposable_types_exist : Failed

STEP 1: instances of disposable types exist
WHEN asked to reset
SHOULD dispose of any disposable instances - FAILED

When_asked_to_dispose_disposable_instances.Given_instances_of_non_disposable_types_exist : Failed

STEP 1: existing instances of non disposable types
WHEN asked to dispose disposable instances
SHOULD set instances to null - FAILED

fix it

	private static void DisposeDisposableInstances()
	{
		foreach (var configurationCollection in Configuration.Values)
		{
			configurationCollection.DisposeDisposableInstances();
		}
	}

Whew. All tests pass and we’ve internally migrated to a container that should let us support multiple configurations for a type. We’ll do that in part 2 of this post.

The code for this project is available from github.

Read Full Post »

In the previous part of this series we built a simple Inversion of Control container called LeafIoC that could only return objects that had default constructors and were specifically configured. In this post we’ll figure out how to support singleton objects in the container.

In a nutshell, here’s a test describing the capability we want (using FluentAssert):

	[Test]
	public void Given_a_type_that_has_been_configured_as_a_singleton()
	{
		Test.Static()
			.When(asked_to_get_an_instance)
			.With(a_type_that_has_been_configured_as_a_singleton)
			.Should(get_a_non_null_instance)
			.Should(get_the_same_instance_every_time)
			.Verify();
	}

Now lets think about the as_a_singleton part for a minute. Do we want a new method on the container like the following?

	public static TInterface GetSingletonInstance<TInterface>()

We could certainly do that but should the user really have to think about whether the object is a singleton or not when asking for one? No, this seems like the wrong end of the system to be specifying a characteristic like Singleton. How about:

	public static void ConfigureSingleton<TInterface, TImplementation>()

This would definitely work but as we add more functionality the public interface of LeafIoC will get really cluttered. What if we also implemented named instances and now we want named static instances? Would we make the name longer?

	public static void ConfigureNamedSingleton<TInterface, TImplementation>(string name)

with variant?

	public static void ConfigureNamedInstance<TInterface, TImplementation>(string name)

No. We need to plan for the ability to compose characteristics using a domain specific language (DSL) with usage like:

	LeafIoC.Configure<ICalculator,ScientificCalculator>()
	          .Named("Scientific")
	          .AsSingleton();

To do that we’re going to have to store some kind of resolver (an object that can evaluate the DSL state at the last possible moment) in the internal dictionary. We already have tests around all the existing capability so lets take this opportunity to refactor the code to better be able to support singletons.

First we’ll create a placeholder class for the resolver:

public class ResolutionInfo
{
}

Then add a property for the object we were previously storing in the dictionary:

public class ResolutionInfo
{
	public MulticastDelegate Initializer { get; set; }
}

Change the dictionary to store it:

	private static readonly Dictionary<Type, ResolutionInfo> Configuration 
		= new Dictionary<Type, ResolutionInfo>();

And fix resultant breakage in LeafIoC:

	public static void Configure<TInterface, TImplementation>() where TImplementation : TInterface, new()
	{
		var type = typeof(TInterface);
		Configuration[type] = new ResolutionInfo
			{
				Initializer = (Func<TInterface>)(() => new TImplementation())
			};
	}

	public static TInterface GetInstance<TInterface>()
	{
		var type = typeof(TInterface);
		ResolutionInfo value;
		if (!Configuration.TryGetValue(type, out value))
		{
			throw new ConfigurationErrorsException(string.Format("No instance of {0} has been configured.", type.FullName));
		}
		var instance = ((Func<TInterface>)(value.Initializer)).Invoke();
		return instance;
	}

Finally, run the tests to make sure we’re still in a good state.

Next, to implement the DSL we’ll have to return something from Configure that we can build upon. First the thing to return:

public class ResolutionContext
{
}

Then the change to Configure():

	public static ResolutionContext Configure<TInterface, TImplementation>() where TImplementation : TInterface, new()
	{
		var type = typeof(TInterface);
		var resolutionInfo = new ResolutionInfo
			{
				Initializer = (Func<TInterface>)(() => new TImplementation())
			};
		Configuration[type] = resolutionInfo;

		return new ResolutionContext(resolutionInfo);
	}

Which forces a change to ResolutionContext

public class ResolutionContext
{
	private readonly ResolutionInfo _resolutionInfo;

	public ResolutionContext(ResolutionInfo resolutionInfo)
	{
		_resolutionInfo = resolutionInfo;
	}
}

Then run the tests to make sure they still pass. Next we’ll drive out the DSL support for AsSingleton(). First a failing test:

[TestFixture]
public class When_asked_to_update_the_resolution_info
{
	[Test]
	public void Given_a_request_to_make_it_singleton()
	{
		Test.Static()
			.When(asked_to_make_it_a_singleton)
			.With(non_singleton_resolution_info)
			.With(context_containing_the_resolution_info)
			.Should(configure_the_resolution_info_to_be_singleton)
			.Should(return_the_context)
			.Verify();
	}
}

the test implementation details:

	private void asked_to_make_it_a_singleton()
	{
		_result = _context.AsSingleton();
	}

	private void configure_the_resolution_info_to_be_singleton()
	{
		_resolutionInfo.IsSingleton.ShouldBeTrue();
	}

	private void context_containing_the_resolution_info()
	{
		_context = new ResolutionContext(_resolutionInfo);
	}

	private void non_singleton_resolution_info()
	{
		_resolutionInfo = new ResolutionInfo
			{
				IsSingleton = false
			};
	}

	private void return_the_context()
	{
		ReferenceEquals(_result, _context).ShouldBeTrue();
	}

which forces the following changes to make it compile:

public class ResolutionInfo
{
	public MulticastDelegate Initializer { get; set; }
	public bool IsSingleton { get; set; }
}

public class ResolutionContext
{
//	... constructor

	public ResolutionContext AsSingleton()
	{
		return null;
	}
}

red

STEP 1: non singleton resolution info
STEP 2: context containing the resolution info
WHEN asked to make it a singleton
SHOULD configure the resolution info to be singleton - FAILED

fix it

public class ResolutionContext
{
//	... constructor

	public ResolutionContext AsSingleton()
	{
		_resolutionInfo.IsSingleton = true;
		return null;
	}
}

run the test again and get red again

STEP 1: non singleton resolution info
STEP 2: context containing the resolution info
WHEN asked to make it a singleton
SHOULD configure the resolution info to be singleton - PASSED
SHOULD return the context - FAILED

fix it

public class ResolutionContext
{
//	... constructor

	public ResolutionContext AsSingleton()
	{
		_resolutionInfo.IsSingleton = true;
		return this;
	}
}

green. Now to implement the functionality we started with at the beginning of this post. First the failing test:

public class When_asked_to_get_an_object_instance
{
	[Test]
	public void Given_a_type_that_has_been_configured_as_a_singleton()
	{
		Test.Static()
			.When(asked_to_get_an_instance)
			.With(a_type_that_has_been_configured_as_a_singleton)
			.Should(get_a_non_null_instance)
			.Should(get_the_same_instance_every_time)
			.Verify();
	}

and the test implementation details

	private void a_type_that_has_been_configured_as_a_singleton()
	{
		LeafIoC.Configure<IComparable, Int32>()
			.AsSingleton();
		_getInstance = () => LeafIoC.GetInstance<IComparable>();
	}

	private void get_the_same_instance_every_time()
	{
		var other = _getInstance();
		ReferenceEquals(_result, other).ShouldBeTrue();
	}

red

STEP 1: a type that has been configured as a singleton
WHEN asked to get an instance
SHOULD get a non null instance - PASSED
SHOULD get the same instance every time - FAILED

To implement this we’re going to have to store the instance once we create it which implies a change to the ResolutionInfo.

public class ResolutionInfo
{
	public MulticastDelegate Initializer { get; set; }
	public bool IsSingleton { get; set; }
	public object Instance { get; set; }
}

Then the change to store the instance if singleton was configured and return the instance if it exists:

	public static TInterface GetInstance<TInterface>()
	{
		var type = typeof(TInterface);
		ResolutionInfo info;
		if (!Configuration.TryGetValue(type, out info))
		{
			throw new ConfigurationErrorsException(string.Format("No instance of {0} has been configured.", type.FullName));
		}

		if (info.Instance != null)
		{
			return (TInterface)info.Instance;
		}

		var instance = ((Func<TInterface>)(info.Initializer)).Invoke();
		if (info.IsSingleton)
		{
			info.Instance = instance;
		}
		return instance;
	}

green.

Are we done? Maybe. Since the container is static the only way it is going to be disposed is if the application is exiting. My understanding is we don’t have to write a finalizer or implement IDisposable on the container because the system will make sure the finalizers get called on all objects.

What about when Reset is called? The system isn’t shutting down but the user wants a fresh start. Who is responsible for making sure the object gets disposed? The user doesn’t expect to have to dispose a singleton so it looks like we’ll have to take responsibility for making sure dispose is called on our singletons when Reset is called.

public class When_asked_to_reset
{
	[Test]
	public void Given_instances_of_disposable_types_exist()
	{
		Test.Static()
			.When(asked_to_reset)
			.With(instances_of_disposable_types_exist)
			.Should(dispose_of_any_disposable_instances)
			.Should(remove_all_exising_configurations)
			.Verify();
	}

	private static void dispose_of_any_disposable_instances()
	{
		((DisposeTester)_instance).Disposed.ShouldBeTrue();
	}

	private static void instances_of_disposable_types_exist()
	{
		LeafIoC.Configure<IDisposable, DisposeTester>().AsSingleton();
		_instance = LeafIoC.GetInstance<IDisposable>();
	}

	public class DisposeTester : IDisposable
	{
		public bool Disposed { get; private set; }
		public void Dispose()
		{
			Disposed = true;
		}
	}

red

STEP 1: instances of disposable types exist
WHEN asked to reset
SHOULD dispose of any disposable instances - FAILED

fix it

	public static void Reset()
	{
		DisposeDisposableInstances();
		Configuration.Clear();
	}

	private static void DisposeDisposableInstances()
	{
		foreach (var resolutionInfo in Configuration.Values)
		{
			var instance = (IDisposable)resolutionInfo.Instance;
			resolutionInfo.Instance = null;
			instance.Dispose();
		}
	}

green. Next test:

	public class NonDisposableTester : IComparable
	{
		public int CompareTo(object obj)
		{
			throw new NotImplementedException();
		}
	}

	[Test]
	public void Given_instances_of_non_disposable_types_exist()
	{
		Test.Static()
			.When(asked_to_reset)
			.With(instances_of_non_disposable_types_exist)
			.Should(remove_all_exising_configurations)
			.Verify();
	}

	private static void instances_of_non_disposable_types_exist()
	{
		LeafIoC.Configure<IComparable, NonDisposableTester>().AsSingleton();
		_instance = LeafIoC.GetInstance<IComparable>();
	}

red

STEP 1: instances of non disposable types exist
WHEN asked to reset - FAILED

System.InvalidCastException : Unable to cast object of type 'NonDisposableTester' to type 'System.IDisposable'.

fix it

	private static void DisposeDisposableInstances()
	{
		foreach (var resolutionInfo in Configuration.Values)
		{
			var instance = resolutionInfo.Instance as IDisposable;
			if (instance == null)
			{
				continue;
			}
			resolutionInfo.Instance = null;
			instance.Dispose();
		}
	}

Go on to the next post: implementing named instances

The code for this project is available from github.

Read Full Post »

If you ever wondered how Inversion of Control tools like StructureMap, NInject, LinFu, Windsor, Unity, Castle, &c are implemented in C# you’ve come to the right place. This is the first of a several exploratory blog posts on this topic.

The simplest use case is getting an instance of an interface where the implementing class has a public, parameterless constructor. This is a good place to start exploring. Consider the following code:


var cache = Container.GetInstance<ICache>();

What do we want to happen when we call GetInstance? It should return a new instance right? How does it know what to return? These are good questions and the answers are… it depends. What if we want the returned instance to be a singleton? Should the system “discover” the appropriate implementation by following a naming convention or should the programmer have to tell it everything. What if we want to change behavior at runtime? What if we want one thing if we’re running in a web environment and a different thing we we’re running in a windows application? This is just a sample of the various usage options available in the various IoC containers.

For now lets keep it really simple and say that the container should return a new instance every time and that the developer has to explicitly configure the returnable implementations. I’ll call this Leaf IoC because the implementing class doesn’t have any dependencies.

Before we can tackle GetInstance we first have to figure out how configuration should work because we won’t be able to test GetInstance without that ability. Let’s think about the test for GetInstance for a minute. First a failing test that describes the behavior we want (using the FluentAssert BDD framework):

[TestFixture]
public class When_asked_to_configure_a_type
{
	[Test]
	public void Given_a_type_that_has_not_been_configured()
	{
		Test.Static()
			.When(asked_to_configure_a_type)
			.With(a_type_that_has_not_been_configured)
			.Should(store_the_requested_configuration)
			.Verify();
	}

How will we know that it succeeded? We need a way to ask the container if a type has been configured right? It looks like we’re going to have to back up a step and implement IsConfigured first:

[TestFixture]
public class When_asked_if_a_type_is_configured
{
	private Func<bool> _isConfigured;
	private bool _result;

	[Test]
	public void Given_a_type_that_has_not_been_configured()
	{
		Test.Static()
			.When(asked_if_a_type_is_configured)
			.With(a_type_that_has_not_been_configured)
			.Should(return_false)
			.Verify();
	}

	private void a_type_that_has_not_been_configured()
	{
		_isConfigured = () => LeafIoC.IsConfigured<IDisposable>();
	}

	private void asked_if_a_type_is_configured()
	{
		_result = _isConfigured();
	}

	private void return_false()
	{
		_result.ShouldBeFalse();
	}
}

Now we can finally implement something:

public static class LeafIoC
{
	private static readonly Dictionary<Type, object> Configuration = new Dictionary<Type, object>();
	public static bool IsConfigured<TInterface>()
	{
		return Configuration.ContainsKey(typeof(TInterface));
	}
}

As you can see we’ll be storing the configuration information in an internal dictionary so that we can find it quickly. We have another test to add for IsConfigured but it requires that Configure exist so let’s jump back to the tests for that method:

[TestFixture]
public class When_asked_to_configure_a_type
{
	[Test]
	public void Given_a_type_that_has_not_been_configured()
	{
		Test.Static()
			.When(asked_to_configure_a_type)
			.With(a_type_that_has_not_been_configured)
			.Should(store_the_requested_configuration)
			.Verify();
	}

	private void asked_to_configure_a_type()
	{
		_configure();
	}

	private void a_type_that_has_not_been_configured()
	{
		_configure = () => LeafIoC.Configure<IComparable, Int32>();
		_expectedType = typeof(Int32);
	}

	private void store_the_requested_configuration()
	{
		var result = LeafIoC.IsConfigured<IComparable>();
		result.ShouldBeTrue();
	}
}

Now let’s look at the implementation:

	public static void Configure<TInterface, TImplementation>() where TImplementation : TInterface, new()
	{
		var type = typeof(TInterface);
		if (!Configuration.ContainsKey(type))
		{
			Configuration.Add(type, (Func<TInterface>)(() => new TImplementation()));
		}
	}

Notice the if statement. What should happen if we’ve already configured the system for a particular TInterface? Let’s say it should replace the existing configuration. How will we test that? We’ll need GetInstance so let’s get the happy path for that working:

[TestFixture]
public class When_asked_to_get_an_object_instance
{
	private Func<object> _getInstance;
	private object _result;

	[Test]
	public void Given_a_type_that_has_been_configured()
	{
		Test.Static()
			.When(asked_to_get_an_instance)
			.With(a_type_that_has_been_configured)
			.Should(get_a_non_null_instance)
			.Should(get_a_different_instance_every_time)
			.Verify();
	}

	private void asked_to_get_an_instance()
	{
		_result = _getInstance();
	}

	private void a_type_that_has_been_configured()
	{
		LeafIoC.Configure<IComparable, Int32>();
		_getInstance = () => LeafIoC.GetInstance<IComparable>();
	}

	private void get_a_non_null_instance()
	{
		_result.ShouldNotBeNull();
	}

	private void get_a_different_instance_every_time()
	{
		_result.ShouldNotBeNull();
		var other = _getInstance();
		ReferenceEquals(_result,other).ShouldBeFalse();
	}

And the implementation

	public static TInterface GetInstance<TInterface>()
	{
		var type = typeof(TInterface);
		object value = Configuration[type];
		return ((Func<TInterface>)value)();
	}

OK, now that we have Configure let’s cycle back to the second test for IsConfigured:

	[Test]
	public void Given_a_type_that_has_been_configured()
	{
		Test.Static()
			.When(asked_if_a_type_is_configured)
			.With(a_type_that_has_been_configured)
			.Should(return_true)
			.Verify();
	}

	private void a_type_that_has_been_configured()
	{
		LeafIoC.Configure<IComparable, Int32>();
		_isConfigured = () => LeafIoC.IsConfigured<IComparable>();
	}

	private void return_true()
	{
		_result.ShouldBeTrue();
	}

Also, now that we have an implementation of GetInstance we can make an improvement to our test for Configure:

	private void store_the_requested_configuration()
	{
		var result = LeafIoC.GetInstance<IComparable>();
		result.GetType().ShouldBeEqualTo(_expectedType);
	}

Next we can tackle the non-happy path of Configure… that being where the interface already has a configuration. We’re going to overwrite it so:

	[Test]
	public void Given_a_type_that_has_already_been_configured()
	{
		Test.Static()
			.When(asked_to_configure_a_type)
			.With(a_type_that_has_already_been_configured)
			.Should(replace_the_existing_configuration)
			.Verify();
	}

	private void a_type_that_has_already_been_configured()
	{
		LeafIoC.Configure<IComparable, Int32>();
		_configure = () => LeafIoC.Configure<IComparable, decimal>();
		_expectedType = typeof(decimal);
	}

	private void replace_the_existing_configuration()
	{
		var result = LeafIoC.GetInstance<IComparable>();
		result.GetType().ShouldBeEqualTo(_expectedType);
	}

And the revised implementation:

	public static void Configure<TInterface, TImplementation>() where TImplementation : TInterface, new()
	{
		var type = typeof(TInterface);
		Configuration[type] = (Func<TInterface>)(() => new TImplementation());
	}

Run the test. Now back to the non-happy path of GetInstance. What should happen if we ask for something that wasn’t configured? I’d say that’s a good time to throw an exception:

	[Test]
	public void Given_a_type_that_has_not_been_configured()
	{
		Test.Static()
			.When(asked_to_get_an_instance)
			.With(a_type_that_has_not_been_configured)
			.ShouldThrowException<ConfigurationErrorsException>()
			.Verify();
	}

	private void a_type_that_has_not_been_configured()
	{
		_getInstance = () => LeafIoC.GetInstance<IDisposable>();
	}

And the revised implementation:

	public static TInterface GetInstance<TInterface>()
	{
		var type = typeof(TInterface);
		object value;
		if (!Configuration.TryGetValue(type, out value))
		{
			throw new ConfigurationErrorsException(string.Format("No instance of {0} has been configured.", type.FullName));
		}
		return ((Func<TInterface>)value)();
	}

One last thing, for completeness we need a way to clear the contents of the container. First the test:

[TestFixture]
public class When_asked_to_reset
{
	[Test]
	public void Given_any_configured_types()
	{
		Test.Static()
			.When(asked_to_reset)
			.With(any_types_already_configured)
			.Should(remove_all_exising_configurations)
			.Verify();
	}

	private static void any_types_already_configured()
	{
		LeafIoC.Configure<IComparable, Int32>();
	}

	private static void asked_to_reset()
	{
		LeafIoC.Reset();
	}

	private static void remove_all_exising_configurations()
	{
		LeafIoC.IsConfigured<IComparable>().ShouldBeFalse();
	}
}

Then the obvious implementation:

	public static void Reset()
	{
		Configuration.Clear();
	}

Go on to the next post: implementing singleton.

The code for this project is available from github.

Read Full Post »

%d bloggers like this: