Okay, in this post we are going to look at Dependency Injection. First thing you have to know (and the reason for the title of this post) is that Inversion of Control is not Dependency Injection. You'll hear developers mix the two terms as if they are the same thing, they're not, Dependency Injection is a "kind of" IoC, but its not the only kind and they are not the same thing.
Right, now we have that out of the way this post is actually about Dependency Injection; so what is DI? Well, normally, when an object has to consume a service, it is that object's responsibility to "know" how to gain access to that service. However, sometimes that is not always desirable; take for example, the instance where you wish to use a different service in test and in production, how will you handle that without code changes? Well, theĀ answer is the DI pattern.
In the DI pattern the object consuming the service merely provides a property into which the required service is "injected" at runtime. In the example below, you can see that two services are created (each implementing the IService interface) and the required Service (specified in a config file) is "injected" into the Consumer at runtime. Using another service is just a case of changing the value in the config file. Try it out for yourself! :-)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Reflection;
namespace IOC
{
/// <summary>
/// GS - I am an intereface that describes a "service"
/// </summary>
public interface IService
{
void addNumbers(int first, int second);
}
/// <summary>
/// I am the service used for testing
/// </summary>
public class TestService : IService
{
#region IService Members
void IService.addNumbers(int first, int second)
{
Console.WriteLine("I am the test service");
int sum = first + second;
Console.WriteLine("{0} + {1} = {2}",first,second,sum);
}
#endregion
}
/// <summary>
/// GS - I am the service used in production
/// </summary>
public class ProductionService : IService
{
#region IService Members
void IService.addNumbers(int first, int second)
{
Console.WriteLine("I am the production service");
int sum = first + second;
Console.WriteLine("{0} + {1} = {2}", first,second,sum);
}
#endregion
}
/// <summary>
/// GS - I am the Consumer and I consume the provided Service
/// </summary>
public class Consumer
{
private int _first;
private int _second;
public Consumer(int first, int second)
{
_first = first;
_second = second;
}
public IService Service { get; set; }
public void consumeService()
{
if (Service == null)
{
throw new Exception("The service has not been set!");
}
Console.WriteLine("Consuming service...");
Service.addNumbers(_first, _second);
}
}
/// <summary>
/// GS - Let's test the pattern
/// </summary>
public class Program
{
static void Main(string[] args)
{
//GS - Create a consumer
Consumer aConsumer = new Consumer(3,7);
//GS - Set the service based on configuration
//GS - Get the type of the required Service
Type ServiceType = Type.GetType(
ConfigurationSettings.AppSettings["ServiceName"]);
//GS - Get the constructor for this Service
ConstructorInfo ci =
ServiceType.GetConstructor(Type.EmptyTypes);
//GS - Instantiate the Service
IService theService = ci.Invoke(null) as IService;
//GS - Inject the Service into the Consumer
aConsumer.Service = theService;
//GS - Consume the service
aConsumer.consumeService();
//GS - Hang around so the user can see what's
//written to the console
Console.Read();
}
}
}