The document discusses dependency injection (DI) and inversion of control (IoC). It defines DI as a design principle where object creation and injection of dependencies is handled by external code rather than the objects themselves. IoC is described as a style where framework code calls application code rather than vice versa. The document outlines several DI patterns like constructor injection and property injection. It also discusses common DI anti-patterns and provides examples of using DI containers to manage object graphs and dependencies.
8. Dependency Injection (DI) definition
It is a design principle in which code
creating a new object supplies the
other objects that are required for it to
function.
Dependency Injection is a set of software
design principles and patterns that enable us to
develop loosely coupled code.
9. Detour - loose coupling
Loose coupling gives
flexibility and ability to swap
concrete implementations
Components should be
independent and have as little
knowledge of other
components of the system as
possible
10. Dependency Injection Benefits
More structured and readable
code
Eliminates unnecessary
dependencies
Simplifies testing
Gives opportunity for better
reuse
Enables parallel development
of features in complex
systems
11. Dependency Injection Purpose
Deliver working software in
the most efficient way
Provide maintainability and
ability to change
Give chance for extension in
the future
12. Dependency Injection Purpose
Deliver working software in
the most efficient way
Provide maintainability and
ability to change
Give chance for extension in
the future
13. DI example
public RavenFileSystem(InMemoryRavenConfiguration config, string name, TransportState receivedTransportState = null)
{
ExtensionsState = new AtomicDictionary<object>();
Name = name;
ResourceName = string.Concat(Constants.FileSystem.UrlPrefix, "/", name);
configuration = config;
try
{
ValidateStorage();
configuration.Container.SatisfyImportsOnce(this);
transportState = receivedTransportState ?? new TransportState();
storage = CreateTransactionalStorage(configuration);
sigGenerator = new SigGenerator();
fileLockManager = new FileLockManager();
BufferPool = new BufferPool(1024 * 1024 * 1024, 65 * 1024);
conflictDetector = new ConflictDetector();
conflictResolver = new ConflictResolver(storage, new CompositionContainer(configuration.Catalog));
notificationPublisher = new NotificationPublisher(transportState);
synchronizationTask = new SynchronizationTask(storage, sigGenerator, notificationPublisher, configuration);
metricsCounters = new MetricsCountersManager();
search = new IndexStorage(name, configuration);
conflictArtifactManager = new ConflictArtifactManager(storage, search);
TimerManager = new ResourceTimerManager();
Tasks = new TaskActions(this, Log);
Files = new FileActions(this, Log);
Synchronizations = new SynchronizationActions(this, Log);
// ...
}
}
ravendb/Raven.Database/FileSystem/RavenFileSystem.cs
14. Common misconceptions about DI
DI is only relevant for late
binding.
DI is only relevant for unit
testing.
DI is a sort of Abstract
Factory on steroids.
DI requires a DI container.
15. TL;DR
DI is nothing more than a collection of design
principles and patterns.
It’s more about a way of thinking and designing
code than it is about tools and techniques.
Important note: loose coupling and DI should be
pervasive in order to be effective. It should be
everywhere in your code base*.
16. DI patterns #1: Constructor Injection
Naturally enforces all required dependencies for a
class; Guard Clauses/Code Contracts prohibit misuse
Usual “go to” recommended approach to use during
everyday work
Hint: think about class constructor as static
dependencies declaration
17. public StreamReader(Stream stream)
: this(stream, true) {
}
public StreamReader(Stream stream, bool detectEncodingFromByteOrderMarks)
: this(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize, false) {
}
public StreamReader(Stream stream, Encoding encoding)
: this(stream, encoding, true, DefaultBufferSize, false) {
}
public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks)
: this(stream, encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize, false) {
}
public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
: this(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, false) {
}
public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen)
{
// ..
}
public StreamReader(String path)
: this(path, true) {
}
public StreamReader(String path, bool detectEncodingFromByteOrderMarks)
: this(path, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize) {
}
public StreamReader(String path, Encoding encoding)
: this(path, encoding, true, DefaultBufferSize) {
}
public StreamReader(String path, Encoding encoding, bool detectEncodingFromByteOrderMarks)
: this(path, encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize) {
}
public StreamReader(String path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
{
// ..
}
coreclr/src/mscorlib/src/System/IO/StreamReader.csCI example
18. Refactoring tightly coupled class to leverage CI
public class Consumer
{
#region Public methods
public void Process()
{
if (AuthenticationService.Instance.Authenticated())
{
var result = ConfigurationService.Instance.GetConfig();
LoggingService.Instance.Log(result);
// ...
}
}
#endregion
}
19. Refactored class with CI
public class Consumer
{
private readonly IAuthenticationService _authenticationService;
private readonly IConfigurationService _configurationService;
private readonly ILoggingService _loggingService;
public Consumer(IAuthenticationService authenticationService,
IConfigurationService configurationService, ILoggingService loggingService)
{
if (authenticationService == null)
throw new ArgumentNullException(nameof(authenticationService));
if (configurationService == null)
throw new ArgumentNullException(nameof(configurationService));
if (loggingService == null)
throw new ArgumentNullException(nameof(loggingService));
_authenticationService = authenticationService;
_configurationService = configurationService;
_loggingService = loggingService;
}
public void Process()
{
if (_authenticationService.Authenticate())
{
var result = _configurationService.GetConfig();
_loggingService.Log(result);
}
}
}
24. /// <summary>
/// Binds a model specified by <paramref name="parameter"/> using <paramref
name="value"/> as the initial value.
/// </summary>
/// <param name="actionContext">The <see cref="ActionContext"/>.</param>
/// <param name="modelBinder">The <see cref="IModelBinder"/>.</param>
/// <param name="valueProvider">The <see cref="IValueProvider"/>.</param>
/// <param name="parameter">The <see cref="ParameterDescriptor"/></param>
/// <param name="metadata">The <see cref="ModelMetadata"/>.</param>
/// <param name="value">The initial model value.</param>
/// <returns>The result of model binding.</returns>
public virtual async Task<ModelBindingResult> BindModelAsync(
ActionContext actionContext,
IModelBinder modelBinder,
IValueProvider valueProvider,
ParameterDescriptor parameter,
ModelMetadata metadata,
object value)
{
// ..
}
MI example Mvc/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Param
eterBinder.cs
25. DI patterns #4: Ambient context
Static property or method
(accessor) providing a
dependency to every module.
The context must be an
abstraction and the respective
property should be writable so
we could leverage DI
26. Example: .NET Tracing
public class AmbientContext
{
public void DoTrace()
{
Trace.Listeners.Add(new ConsoleListener());
Trace.WriteLine("Traced message");
}
private class ConsoleListener : TraceListener
{
public override void Write(string message)
{
Console.Write("Custom listener message: " + message);
}
public override void WriteLine(string message)
{
Console.WriteLine("Custom listener message: " + message);
}
}
}
27. DI patterns #4: Ambient context
public class RealDateTimeProvider : IDateTimeProvider
{
public DateTime Today() { return DateTime.Today; }
}
public static class DateTimeProvider
{
private static IDateTimeProvider _currentDateTimeProvider;
public static IDateTimeProvider Current
{
get { return _currentDateTimeProvider ?? Default; }
set { _currentDateTimeProvider = value; }
}
public static IDateTimeProvider Default = new RealDateTimeProvider();
}
// Context usage:
var today = DateTimeProvider.Current.Today();
// Altering AC if needed:
DateTimeProvider.Current = new MockDateTimeProvider();
28. DI antipatterns. #1- Control Freak
Class controlling all of its
dependencies by explicitly
instantiating them
Red flag: new keyword all over the
code
Results in inability to control objects
lifetime and being constraint to a
current (tightly coupled)
implementation
Refactoring to interfaces, but still
instantiating objects inside class
does not result in proper DI
29. DI antipatterns. #2 – Bastard injection
(Test-specific) constructor
defaulting some of the class
dependencies
Increases the amount of implicit
dependencies
Makes choice between constructor
ambiguous, can cause issues with
IoC containers
Result of developers’ attempt to
make code testable without full
understanding of DI*
31. DI antipatterns. #3 – Constrained construction
Require a dependency to have a
specific constructor (and throw
exception when mandatory
configuration is not available)
Improper solution for achieving
late binding
32. DI antipatterns. #4 – Service Locator
Although introduced by Martin
Fowler, is considered an anti-
pattern:
- Hides dependencies
- Causes runtime errors instead
of compile-time
- Amplifies the possibility of
introducing breaking changes
during maintenance
Service Locator is an Anti-Pattern by Mark Seemann
http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/
SOLID Encapsulation
33. Fighting Dealing with DI anti-patterns
• Decouple logic into separate classes
• Extract interfaces/abstract base classes
• Investigate object runtime behavior, try to
make decision on implementation choosing
as late as possible
• Defer objects creation to Factories
• Don’t forget about created objects lifecycle
(dispose etc.)
• Monitor coupling (NDepend and other tools)
• Think strategy over tactics, don’t over-
refactor (o_O)
34. Inversion of Control
Inversion of Control (IoC) as a
style of programming in which
the framework takes the control
of the flow instead of your code.
IoC is also known as the
Hollywood Principle: “Don't call
us, we'll call you”
NB: has nothing to do with actual
dependencies passing
35. Inversion of Control
IoC is an attribute of a
framework. A library may have or
may not have IoC, framework
always will.
DI helps achieve IoC
Martin Fowler - Inversion of Control Containers and the Dependency Injection patte
https://www.martinfowler.com/articles/injection.html
36. IoC Example – ASP.NET Core Startup.cs
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit
// http://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", true)
.Build();
var connectionString = config.GetConnectionString("DefaultConnection");
var optionsBuilder = new DbContextOptionsBuilder().UseSqlServer(connectionString);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseStaticFiles();
loggerFactory.AddConsole();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} app.UseMvc();
}
}
37. Inversion of Control
IoC defers the responsibility of dependencies
creation to a higher level code.
This way we substitute composition with
aggregation and move some of the problems out
of the particular class.
Ideally this responsibility should be propagated to
a single point – so-called Composition Root
38.
39. Inversion of Control
IoC (the same as DI and other engineering
practices) is not a goal on itself
IoC as design principle that targets complexity
and helps simplify maintenance of big solutions
If it is misused or exaggerated, one should not
expect positive effect
40. The most usual IoC offender…
… Control Freak/
God Object
41. DI/IoC Containers
In real world applications, a single class
can have many dependencies, each of
which have their own dependencies
that forms a large graph.
That’s the place where DI container
comes to the rescue.
A DI container should resolve the
dependencies, and this is where the
decision of selecting a concrete class
for the given abstraction should be
made.
42. NB: “DI” vs. “IoC” containers
Calling container “DI” have
mostly historical roots.
Since IoC is a broader term,
and also because modern
containers support scope
management, lifecycle
management, calls interception
and delayed initialization which
does not fit well into DI
paradigm, it’s more correct to
call them “IoC containers”.
47. “Building an IoC container in 15 lines of code”
1) Configuration
2) Lifecycle management
3) Registering service bindings
4) Handling instance activation
50. Which container should I choose?
• Should it be cross-platform?
• Single vs. multi-threaded resolving
51. Which container should I choose?
• Should it be cross-platform?
• Single vs. multi-threaded resolving
• Code vs. XML vs. auto-
configuration capabilities
52. Which container should I choose?
• Should it be cross-platform?
• Single vs. multi-threaded resolving
• Code vs. XML vs. auto-
configuration capabilities
• Do you encounter complex
scenarios that will require custom
objects lifetime management?
53. Which container should I choose?
• Should it be cross-platform?
• Single vs. multi-threaded resolving
• Code vs. XML vs. auto-
configuration capabilities
• Do you encounter complex
scenarios that will require custom
objects lifetime management?
• Do you need generics/IEnumerable
support?
54. Which container should I choose?
• Should it be cross-platform?
• Single vs. multi-threaded resolving
• Code vs. XML vs. auto-
configuration capabilities
• Do you encounter complex
scenarios that will require custom
objects lifetime management?
• Do you need generics/IEnumerable
support?
• Will you do complex property/child
containers resolving?
55. Which container should I choose?
• Should it be cross-platform?
• Single vs. multi-threaded resolving
• Code vs. XML vs. auto-
configuration capabilities
• Do you encounter complex
scenarios that will require custom
objects lifetime management?
• Do you need generics/IEnumerable
support?
• Will you do complex property/child
containers resolving?
• Interception proxy
56. Which container should I choose?
• Should it be cross-platform?
• Single vs. multi-threaded resolving
• Code vs. XML vs. auto-
configuration capabilities
• Do you encounter complex
scenarios that will require custom
objects lifetime management?
• Do you need generics/IEnumerable
support?
• Will you do complex property/child
containers resolving?
• Interception proxy
62. Rule of Thumb
The container is used properly when the
application is completely unaware of it’s
existence.
63. Rule of Thumb
The container is used properly when the
application is completely unaware of it’s
existence.
64. References:
• Baharestani Daniel - Mastering Ninject for Dependency Injection (Packt Publishing, 2013)
• Seemann Mark - Dependency Injection in .NET (Manning, 2012)
• Martin Fowler - Inversion of Control Containers and the Dependency Injection pattern
(https://www.martinfowler.com/articles/injection.html)
• James Shore - Dependency Injection Demystified (http://www.jamesshore.com/Blog/Dependency-Injection-
Demystified.html)
• Ayende @ Rahien blog (https://ayende.com)
• Sergey Teplyakov blog (http://sergeyteplyakov.blogspot.com)
• Eric Lippert - Immutability in C# Part One: Kinds of Immutability
(https://blogs.msdn.microsoft.com/ericlippert/2007/11/13/immutability-in-c-part-one-kinds-of-immutability/)
• Mark Seemann - Service Locator is an Anti-Pattern (http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-
Pattern/)
• Daniel Palme - IoC Container Benchmark - Performance comparison
• (http://www.palmmedia.de/Blog/2011/8/30/ioc-container-benchmark-performance-comparison,
https://github.com/danielpalme/IocPerformance)
• Andrey Akinshin – Benchmark.NET (https://github.com/dotnet/BenchmarkDotNet)
• RavenDB (https://github.com/ravendb/ravendb)
• ASP.NET Core MVC (https://github.com/aspnet/Mvc/)
• Core CLR (https://github.com/dotnet/coreclr)
• Jeremy Clark - Dependency Injection On-Ramp Pluralsight course
(https://app.pluralsight.com/library/courses/dependency-injection-on-ramp/table-of-contents)
• John Sonmez - Practical IoC With ASP.NET MVC 4 Pluralsight course
(https://app.pluralsight.com/library/courses/ioc-aspdotnet-mvc4/table-of-contents)