Dependency Injection with .NET
-OR-
So you’ve decided to finally inject
your dependencies
-OR-
Let’s test production scenarios so it
doesn’t break
Overview
• What is DI?
• Why use DI?
• Where to use DI?
• How to do DI?
• C#, Legacy Code, and DI
– Interfaces, and Singletons, and Mocks, oh my!
What is Dependency Injection
• A software design pattern that implements inversion of
control for resolving dependencies. A dependency is an
object that can be used (a service). An injection is the passing
of a dependency to a dependent object (a client) that would
use it.
– Wikipedia
AppIoC
Dependency
Concrete
Implementation
Concrete
Implementation
Concrete
Implementation
Dependency Inject – An Example
• Legacy code w/o DI
public class ReviewManager{
public ElasticSearchBackedReviewRepository ReviewsRepo
{get; set;}
public void GetReviewsByMovie( int movieId){
return
ElasticSearchBackedReviewRepository.GetReviews(movieId);
}
}
• Legacy code w/o DI
public class ReviewManager{
public IReviewRepository ReviewsRepo {get; set;}
Public ReviewManager( IReviewRepository repo ){
ReviewsRepo = repo;
}
public void GetReviewsByMovie( int movieId){
return ReviewsRepo.GetReviews(moviedId);
}
}
AppIoC
Dependency
Concrete
Implementation
Concrete
Implementation
Concrete
Implementation
Why Interfaces?
• Develop against a contract
– Business logic isn’t changed as new
implementations are introduced
• Moq (for unit tests) requires interfaces or
virtual methods.
• All wiring for dependencies can be performed
in a centralized location
Why Mocking?
• Allows testing any use case without the need
for test data
– Mock a response that returns the use case you are
testing (as opposed to test data in the DB)
– Null return values
– Timeout (web service clients)
– 0 reviews, 1 review, 10000 reviews
When? Architecture Boundaries
App (Front End)
App (Business Logic)
DB 1
DB 2
Service 1
Service 1
Service 1
Any time the app reaches out for data is a good candidate for dependency injection.
In this scenario, the clients we use to wrap the 2 databases and the 3 services
would each implement an interface that could be mocked for unit tests
When? Responsibility Boundaries
• Fandango Desktop/Mobile Web
– Reviews (Direct DB calls, Elastic search service)
– Movies (DB calls, movie service)
– Theaters (DB calls, Commerce service, old service,
new API)
DI via Delegates
• Initial Effort is low
– No extra frameworks
– unit test can define the mocked method
• Subsequent efforts are O(N)
– One change per new mocked method
• Pros
– Methods can be incorporated one at a time
• Cons
– Each new method will require its own delegate and
new wiring.
DI via Mock POCOs
• Initial Effort is medium
– New class for each mock
– Logic for mock states
• Subsequent efforts
– Potential for new forks (and bugs) in mock POCO logic for
each use case
• Pros
– Don’t need to learn a new framework.
• Cons
– Supporting all use cases increases potential for bugs in the
mocks.
– Some dependencies can’t be mocked
1
1 – With Moq, you can only mock methods defined in an interface or virtual methods.
DI with Mocks (Moq)
• Initial Effort is high
– Each unit test use case requires its own moq wrapper
per dependency
• Subsequent changes
– Only changes to the contract require modifications to
your mocks
• Pros
– Each tests clearly identifies its assumptions
• Cons
– Lots of unit test code will just be setting up the
dependency
Legacy Code: Static Methods
• Interfaces can’t have static methods
• Convert static methods to instance methods
that conform to the (new) interface with a
Singleton to expose the static instance.
Scenario
Production issue with unknown cause
• Scenario in production we can’t reproduce
easily
• In a unit test, mock the underlying interface to
the throw the same exception.
– At the very least, we’ll know how to stop this error
from crashing the entire app and set up proper
logging to identify potential causes
Scenario
Production issue with unknown cause
[Fact]
public void TestIndex_WebExceptionFromReviewManager()
{
InjectCookieCollectionDependency();
var mock = new Mock<IReviewManager>();
mock.Setup(rm => rm.GetEsReviewsByMovie(
It.IsAny<int>(),
It.IsAny<int>(),
It.IsAny<int>(),
It.IsAny<UserReviewSort>()))
.Throws(new WebException());
IReviewManager reviewManager = mock.Object;
using (var controller = new MovieController(reviewManager) { ControllerContext = new ControllerContext { HttpContext = new
MockHttpContext() } })
{
MockHttpContext httpContext = configureContext(UserAgentValue);
controller.ControllerContext.HttpContext = httpContext;
ActionResult result = controller.Index(MovieIdAmericanBeauty);
Assert.IsType(typeof (ViewResult), result);
}
}
Scenario
Anonymous + authenticated users
• Our apps identify a logged in user by the
presence of a cookie. We can mock that!
– See changes to MovieController with
ICustomerCookieProvider
Strategies for Unit Testing
• New Projects
– TDD allows us to work with QA to identify good
test cases before we start writing code
• Legacy Projects
– Don’t test something that’s been working in
production for years
– Create unit tests to reproduce your bug. This will
allow for immediate ROI on the tests
– Introduce tests for new features
Side Effect (Good)
• Enforces the SOLID principles
Single Responsibility [link]
If an interface crosses domains, you know to split them
Multiple interfaces  too many responsibilities for the class
Open/Closed Principle [link]
Your code is inherently open to extension
Liskov Substitution Principle [link]
Your mocks provide are substituted for the concrete
implementations without code changes
Interface segregation Principle [link]
Interface contract hides helper methods
Dependency inversion principle [link]
This is what we’re talking about here
Further Reading
• Martin Fowler (http://martinfowler.com/articles/injection.html)
• AutoFac IoC container(http://autofac.org/)
• AutoFac in actionNuGet Gallery – DI by Environment

Dependency Injection in .NET applications

  • 1.
    Dependency Injection with.NET -OR- So you’ve decided to finally inject your dependencies -OR- Let’s test production scenarios so it doesn’t break
  • 2.
    Overview • What isDI? • Why use DI? • Where to use DI? • How to do DI? • C#, Legacy Code, and DI – Interfaces, and Singletons, and Mocks, oh my!
  • 3.
    What is DependencyInjection • A software design pattern that implements inversion of control for resolving dependencies. A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it. – Wikipedia AppIoC Dependency Concrete Implementation Concrete Implementation Concrete Implementation
  • 4.
    Dependency Inject –An Example • Legacy code w/o DI public class ReviewManager{ public ElasticSearchBackedReviewRepository ReviewsRepo {get; set;} public void GetReviewsByMovie( int movieId){ return ElasticSearchBackedReviewRepository.GetReviews(movieId); } } • Legacy code w/o DI public class ReviewManager{ public IReviewRepository ReviewsRepo {get; set;} Public ReviewManager( IReviewRepository repo ){ ReviewsRepo = repo; } public void GetReviewsByMovie( int movieId){ return ReviewsRepo.GetReviews(moviedId); } } AppIoC Dependency Concrete Implementation Concrete Implementation Concrete Implementation
  • 5.
    Why Interfaces? • Developagainst a contract – Business logic isn’t changed as new implementations are introduced • Moq (for unit tests) requires interfaces or virtual methods. • All wiring for dependencies can be performed in a centralized location
  • 6.
    Why Mocking? • Allowstesting any use case without the need for test data – Mock a response that returns the use case you are testing (as opposed to test data in the DB) – Null return values – Timeout (web service clients) – 0 reviews, 1 review, 10000 reviews
  • 7.
    When? Architecture Boundaries App(Front End) App (Business Logic) DB 1 DB 2 Service 1 Service 1 Service 1 Any time the app reaches out for data is a good candidate for dependency injection. In this scenario, the clients we use to wrap the 2 databases and the 3 services would each implement an interface that could be mocked for unit tests
  • 8.
    When? Responsibility Boundaries •Fandango Desktop/Mobile Web – Reviews (Direct DB calls, Elastic search service) – Movies (DB calls, movie service) – Theaters (DB calls, Commerce service, old service, new API)
  • 9.
    DI via Delegates •Initial Effort is low – No extra frameworks – unit test can define the mocked method • Subsequent efforts are O(N) – One change per new mocked method • Pros – Methods can be incorporated one at a time • Cons – Each new method will require its own delegate and new wiring.
  • 10.
    DI via MockPOCOs • Initial Effort is medium – New class for each mock – Logic for mock states • Subsequent efforts – Potential for new forks (and bugs) in mock POCO logic for each use case • Pros – Don’t need to learn a new framework. • Cons – Supporting all use cases increases potential for bugs in the mocks. – Some dependencies can’t be mocked 1 1 – With Moq, you can only mock methods defined in an interface or virtual methods.
  • 11.
    DI with Mocks(Moq) • Initial Effort is high – Each unit test use case requires its own moq wrapper per dependency • Subsequent changes – Only changes to the contract require modifications to your mocks • Pros – Each tests clearly identifies its assumptions • Cons – Lots of unit test code will just be setting up the dependency
  • 12.
    Legacy Code: StaticMethods • Interfaces can’t have static methods • Convert static methods to instance methods that conform to the (new) interface with a Singleton to expose the static instance.
  • 13.
    Scenario Production issue withunknown cause • Scenario in production we can’t reproduce easily • In a unit test, mock the underlying interface to the throw the same exception. – At the very least, we’ll know how to stop this error from crashing the entire app and set up proper logging to identify potential causes
  • 14.
    Scenario Production issue withunknown cause [Fact] public void TestIndex_WebExceptionFromReviewManager() { InjectCookieCollectionDependency(); var mock = new Mock<IReviewManager>(); mock.Setup(rm => rm.GetEsReviewsByMovie( It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<UserReviewSort>())) .Throws(new WebException()); IReviewManager reviewManager = mock.Object; using (var controller = new MovieController(reviewManager) { ControllerContext = new ControllerContext { HttpContext = new MockHttpContext() } }) { MockHttpContext httpContext = configureContext(UserAgentValue); controller.ControllerContext.HttpContext = httpContext; ActionResult result = controller.Index(MovieIdAmericanBeauty); Assert.IsType(typeof (ViewResult), result); } }
  • 15.
    Scenario Anonymous + authenticatedusers • Our apps identify a logged in user by the presence of a cookie. We can mock that! – See changes to MovieController with ICustomerCookieProvider
  • 16.
    Strategies for UnitTesting • New Projects – TDD allows us to work with QA to identify good test cases before we start writing code • Legacy Projects – Don’t test something that’s been working in production for years – Create unit tests to reproduce your bug. This will allow for immediate ROI on the tests – Introduce tests for new features
  • 17.
    Side Effect (Good) •Enforces the SOLID principles Single Responsibility [link] If an interface crosses domains, you know to split them Multiple interfaces  too many responsibilities for the class Open/Closed Principle [link] Your code is inherently open to extension Liskov Substitution Principle [link] Your mocks provide are substituted for the concrete implementations without code changes Interface segregation Principle [link] Interface contract hides helper methods Dependency inversion principle [link] This is what we’re talking about here
  • 18.
    Further Reading • MartinFowler (http://martinfowler.com/articles/injection.html) • AutoFac IoC container(http://autofac.org/) • AutoFac in actionNuGet Gallery – DI by Environment

Editor's Notes

  • #13 See Beyond Compare saved session #1