About the book
This is a sample chapter
of the book Dependency Injection in .NET. It has been
published with the exclusive
permission of Manning.
Get 30% discount
DotNetSlacker readers can get 30% off the full print book or ebook at www.manning.com using the promo code
dns30 at checkout.
Dependency Injection (DI) is a technique and a set of design patterns that enable us to develop loosely coupled code. Instead of directly instantiating concrete
classes whenever we need to invoke a particular service, we program against interfaces. Instances of these interfaces are injected into the code by the external client
so that the code can vary independently of the Dependency.
Here we will take a detailed look at the DI pattern known as Constructor Injection.
Since it is a design pattern, I have chosen to present it in the formal design pattern style.
How do we guarantee that a necessary Dependency is always
available to the class we are currently developing?
By requiring all callers to supply the Dependency as a parameter to the class' constructor.
Figure 1: NeedyClass needs an instance of Dependency to work, so it requires any Client to supply an instance via its constructor. This guarantees that the
instance is available to NeedyClass whenever it is needed.
When a class requires an instance of a Dependency to work at all, we can supply that Dependency through the class'
constructor, enabling it to store the reference for future (or immediate) use.
How it works
The class that needs the Dependency must expose a public constructor that takes an instance of the required Dependency as a constructor argument. In most cases,
this should be the only available constructor. If more than one Dependency is needed, additional constructor arguments can be used.
Listing 1: A constructor injection
The Dependency (in listing A that would be the abstract DiscountRepository class) is a required constructor argument. Any client code that does not
supply an instance of the Dependency simply cannot compile. However, since both interfaces and abstract classes are reference types, a caller can pass in
as an argument to make the calling code compile, so we need to protect the class against such misuse with a Guard Clause.
The combined efforts of the
compiler and the Guard Clause guarantees that the constructor argument is valid if no exception is thrown, so at this point, the constructor can save the Dependency
for future use without knowing anything about the real implementation.
It is good practice to mark the field holding the Dependency as
this guarantees that once the initialization logic of the constructor has executed, the field cannot be modified. This is not strictly required from a DI point of
view, but it protects you from accidentally modifying the field (such as setting it to
null) somewhere else in the depending class' code.
constructor has returned, the new instance of the depending class is in a consistent state with a proper instance of its Dependency injected into it. Since it holds a
reference to this Dependency it can use it as often as necessary from any of its other members. It does not need to test for null, because the instance is guaranteed
to be present.
When to use it
Constructor Injection should be your default choice for DI. It addresses the most common scenario where a class requires one or more Dependencies, and no
reasonable Local Defaults are available.
Constructor Injection addresses that scenario very well because it guarantees that the Dependency is present. If the
depending class absolutely cannot function without the Dependency, that guarantee is valuable.
In cases where the local library can supply a good default
implementation, Property Injection may be a better fit, but this is often not the case. In the earlier chapters, I have shown many examples of Repositories as
Dependencies, and these are good examples of Dependencies where the local library can supply no good default implementation, since the proper implementations belong in
specialized Data Access libraries.
Table 1: Constructor injection advantages and disadvantages
Injection is guaranteed
Requires up-front wiring of entire dependency graph
Easy to implement
Apart from the guaranteed injection already discussed, this pattern is also very easy to implement using the four steps implied by listing 4.1.
main disadvantage to Constructor Injection is that it requires that the entire dependency graph is initialized immediately – often at application startup. We saw that
in listing 3.4: to create the custom ControllerFactory, I have to create all of its Dependencies. One of these is a BasketDiscountPolicy, but the implementation I want
to use has its own Dependency that also needs to be initialized. Even in this relatively simple example, I need to initialize six Dependencies before I can create a
When we use a DI Container that supports Auto-wiring, this is not particularly difficult to define in code, but the actual act of
initializing all these objects still takes time. Depending on how complex the dependency graph is, this may cause the application startup time to be slower than it
otherwise would be.
Although Constructor Injection tends to be ubiquitous in applications employing DI, it is not very present in the .NET Base Class Library (BCL). This is mainly
because the BCL is a set of libraries and not a full-fledged application.
Two related examples where we can see a sort of Constructor Injection in the BCL is
with System.IO.StreamReader and System.IO.StreamWriter that both take a System.IO.Stream instance in their constructors. They also have a lot of overloaded
constructors that take a file path instead of a Stream instance, but these are just convenience methods that internally create a FileStream based on the specified file
path – here are all the StreamWriter constructors, but the StreamReader constructors are similar:
The Stream class is an abstract class that serves as an Abstraction upon which StreamWriter and StreamReader operates to perform their duties.
You can supply any Stream implementation in their constructors and they will use it, but they will throw ArgumentNullExceptions if you try to slip them a null
While the BCL can provide us with examples where we can see Constructor Injection in use, it is always more instructive to see an example, so the next
section walks you through a full implementation example.
Example: Adding a currency provider to the shopping basket
I would like to add a new feature to the sample commerce application I expanded in chapter 3 – namely the ability to perform currency conversions. I will spread the
example throughout this chapter to demonstrate the different DI patterns in play, but when I'm done, the Home page should look like figure 2.
Figure 2: The sample commerce application with currency conversion implemented. The user can now select among three different currencies, and both product
prices and basket totals will be displayed in that currency.
One of the first things we need is a CurrencyProvider – a Dependency that can provide us with the currencies we request.
Let's define it like this:
The Currency class is another abstract class that provides conversion rates between itself and other currencies:
We need the currency conversion feature in both the HomeController and the BasketController, and since both implementations are quite similar, I will
show you only the BasketController.
A CurrencyProvider is very likely to represent an out-of-process resource, such as a web service or a database that can
supply conversion rates. This means that it would be most fitting to implement a concrete CurrencyProvider in a separate project (such as a Data Access library).
Hence, there is no reasonable Local Default. At the same time, the BasketController class will need a CurrencyProvider to be present, so Constructor Injection is a
good fit. Listing 2 shows how the CurrencyProvider Dependency is injected into the BasketController.
Listing 2: Injecting a CurrencyProvider into the BasketController
The BasketController class already had a Dependency on IBasketService, so I simply add the new CurrencyProvider Dependency as a second constructor
argument and then follow the same sequence outlined in listing 1: Guard Clauses guarantee that the Dependencies are not null, which means it is safe to store them for
later use in read-only fields.
Now that since the CurrencyProvider is guaranteed to be present in the BasketController, it can be used from anywhere within – for
example in the
I haven't yet discussed the CurrencyProfileService, so for now, just know that it provides the current user's preferred currency code. In section 4.2.4 I
will discuss the CurrencyProfileService in greater detail.
Given a currency code, the CurrencyProvider can be invoked to provide a Currency that represents that
code. Notice that I can simply use the currencyProvider field without needing to check it in advance, because it is guaranteed to be present.
Now that I have the
Currency, I can then proceed to perform the rest of the work in the Index method, but I have not yet shown that implementation. As we progress through this chapter, I
will built on this method and add more currency conversion functionality along the way.
Constructor Injection is the most generally applicable DI pattern available, and also the easiest to implement correctly. It applies when the Dependency is
If we need to make the Dependency optional, we can change to Property Injection if we have a proper Local Default.
When the Dependency
represents a Cross-Cutting Concern that should be potentially available to any module in the application, we can use an Ambient Context instead.
There is more
than one way to implement DI in a single class, but Constructor Injection is a good default choice. It is simple to implement because we simply define the class'
constructor so that it requires the Dependency as a constructor parameter. This forces all callers to supply the Dependency and helps enforce as an invariant that the
Dependency has been supplied and is always available.
Manning Publication publishes computer books for professionals--programmers, system administrators, designers, architects, managers and others. Our focus is on computing titles at professional levels. We care about the quality of our books. We work with our authors to coax out of them the best writi...
This author has published 33 articles on DotNetSlackers. View other articles or the complete profile here.
Please login to rate or to leave a comment.