This document provides an introduction to dependency injection. It explains that dependency injection allows code to request dependencies rather than create them directly, making code more flexible and testable. It demonstrates how to define interfaces for dependencies and configure a dependency injection container to resolve them. Writing unit tests is easier with dependency injection because mock dependencies can be passed in instead of real implementations. The document also discusses additional features of dependency injection containers like lifetime management and configuration options.
2. What is this thing?
• Dependency Injection (also called Inversion of Control) is a technique for
providing the things your code needs at runtime.
• Relies very heavily on abstraction layers (interfaces, base classes)
• When used properly, can make your code simpler and easier to test!
3. Let’s Start at the Beginning
public class MyBusinessLogic
{
public MyBusinessLogic()
{
}
public void DoesSomethingInteresting(MyDataItem item)
{
MyDataAccessLayer foo = new MyDataAccessLayer();
foo.AddItem(item);
}
}
4. Why is this a problem?
• It ties us to a particular data access layer
• What if I want to be able to support a different database engine?
• What if I want to use a Web Service?
• Etc.
• It’s hard to test
• I can’t verify the code works unless I have a database
• It takes time to set the database up
• What happens when more than one person wants to run a test at the same time?
5. How can we fix this?
• Instead of hard-coding the use of MyDataAccessLayer let’s make things
more flexible
• Define a new interface: IMyDataAccessLayer
• Visual Studio makes this pretty easy; right-click the class and choose “Extract Interface
• Use this interface instead of the concrete class
6. How can we fix this (contd.)?
public class MyBusinessLogic
{
IMyDataAccessLayer _myDAL;
public MyBusinessLogic(IMyDataAccessLayer myDAL)
{
_myDAL = myDAL;
}
public void DoesSomethingInteresting(MyDataItem item)
{
_myDAL.AddItem(item);
}
}
7. How can we get this into our class?
• Simplest way: supplying it to the constructor (aka Constructor injection)
• This works, but isn’t much better than the original
• Better way: use a Dependency Injection Container
• Unity
• Ninject
• SimpleIOC
• etc.
var myBLL = new MyBusinessLogic(new MyDataAccessLayer)
8. Using a DI Container
• Most containers use a registration mechanism mapping type-to-type:
• Resolve types at runtime:
myContainer.RegisterType(typeof(IFoo), typeof(Foo))
myContainer.RegisterType<IFoo, Foo>()
var instance = myContainer.Resolve(typeof(IFoo))
var instance = myContainer.Resolve<IFoo>()
9. But wait, there’s more!
• Every DI container supports dependency chains
• If registered type A takes an instance of registered type B as a constructor argument, then
an instance of B will be automatically created:
• Many frameworks use DI containers to automatically resolve types
• For example, ASP.NET MVC’s Dependency Resolver
• NuGet makes this very easy - Unity bootstrapper
myContainer.RegisterType<IMyBusinessLogic, MyBusinessLogic>();
myContainer.RegisterType<IMyDataAccessLayer, MyDataAccessLayer>();
var myBLL = myContainer.Resolve(IMyBusinessLogic);
11. Where does “testable” come in?
• Classes that take dependencies as configuration are much easier to test!
• This is where the abstraction layer comes in very handy!
• Rather than supply that dependency, pass something that looks like it.
• Simplest approach: derive a “fake” class from the base class/interface
• Better approach: use a mocking framework like Moq!
• Code/configure your fake to behave the way you expect it to, and assert that
*your* code behaves as it should.
12. The concept:
• The two-port model
• For a given input, you expect a certain output
Your CodeInput Output
13. DEMO: USING MOQ IN A UNIT TEST
Verifying that the controller does what it should
14. What else can we do with this?
• Once you start thinking in terms of dependencies, you can start making your
code a LOT cleaner.
• AKA “Separation of Concerns” – have classes that do one type of thing and have other
classes use them as Lego blocks to build complex systems.
• Deliver your code faster
• Defining the “contract” in terms of interfaces and expected behavior allows different people
to work in parallel!
• Make your own code easier to maintain
• If each class has its own job, and you have interfaces defining the API, you can change
implementations without modifying the calling code
• Instead of writing one-off test apps, write unit tests
15. Stupid Container Tricks
• Lifetime Management
• LifetimeManager
• A way to create a singleton from any class
• Named registrations
• RegisterType<T>(“Name”)
• ResolveAll<T>()
• Can be used as an extensibility point – “plugins”
• XML Configuration
• Rather than declaring everything in code, use an XML file
• Discovery
• Use reflection to find all types that implement a given interface and register them
• Microsoft Extensibility Framework
16. References
• Microsoft Unity
• https://unity.codeplex.com/
• “Dependency Injection with Unity”
http://www.microsoft.com/en-us/download/details.aspx?id=39944
• Moq
• https://github.com/Moq/moq4