Reader small image

You're reading from  Architecting ASP.NET Core Applications - Third Edition

Product typeBook
Published inMar 2024
Reading LevelIntermediate
PublisherPackt
ISBN-139781805123385
Edition3rd Edition
Languages
Right arrow
Author (1)
Carl-Hugo Marcotte
Carl-Hugo Marcotte
author image
Carl-Hugo Marcotte

Carl-Hugo Marcotte is a software craftsman who has developed digital products professionally since 2005, while his coding journey started around 1989 for fun. He has a bachelor's degree in computer science. He has acquired a solid background in software architecture and expertise in ASP.NET Core through creating a wide range of web and cloud applications, from custom e-commerce websites to enterprise applications. He served many customers as an independent consultant, taught programming, and is now a Principal Architect at Export Development Canada. Passionate about C#, ASP.NET Core, AI, automation, and Cloud computing, he fosters collaboration and the open-source ethos, sharing his expertise with the tech community.
Read more about Carl-Hugo Marcotte

Right arrow

Dependency Injection

This chapter explores the ASP.NET Core dependency injection (DI) system, how to leverage it efficiently, and its limits and capabilities.

We learn to compose objects using DI and delve into the inversion of control (IoC) principle. As we traverse the landscape of the built-in DI container, we explore its features and potential uses.

Beyond practical examples, we lay down the conceptual foundation of DI to understand its purpose, its benefits, and the problems it solves and to lay down the ground for the rest of the book as we rely heavily on DI.

We then return to the first three Gang of Four (GoF) design patterns we encountered, but this time, through the lens of DI. By refactoring these patterns using DI, we gain a more holistic understanding of how this powerful design tool influences the structure and flexibility of our software.

DI is a cornerstone for mastering modern application design and plays a transformative role in developing efficient...

What is dependency injection?

DI is a way to apply the IoC principle. IoC is a broader version of the dependency inversion principle (the D in SOLID). In essence, while IoC is about shifting the control of the program’s flow to a separate component or framework to increase modularity, the DIP focuses on how modules or classes are interconnected.

The idea behind DI is to move the creation of dependencies from the objects themselves to the composition root. That way, we can delegate the management of dependencies to an IoC container, which does the heavy lifting.

An IoC container and a DI container are the same thing—they’re just different words people use. I use both interchangeably in real life, but I stick to the IoC container in the book because it seems more accurate than a DI container.

IoC is the concept (the principle), while DI is a way of inverting the flow of control (applying IoC). For example, you apply the IoC principle (inverting...

Revisiting the Strategy pattern

In this section, we leverage the Strategy pattern to compose complex object trees and use DI to dynamically create those instances without using the new keyword, moving away from being control freaks and toward writing DI-ready code.

The Strategy pattern is a behavioral design pattern we can use to compose object trees at runtime, allowing extra flexibility and control over objects’ behavior. Composing our objects using the Strategy pattern makes our classes smaller, easier to test and maintain, and puts us on the SOLID path.

From now on, we want to compose objects and lower the amount of inheritance to a minimum. We call that principle composition over inheritance. The goal is to inject dependencies (composition) into the current class instead of depending on base class features (inheritance).

Additionally, this approach enables us to pull out behaviors and place them in separate classes, adhering to the Single Responsibility Principle...

Revisiting the Singleton pattern

The Singleton pattern is obsolete, goes against the SOLID principles, and we replace it with a lifetime, as we’ve already seen. This section explores that lifetime and recreates the good old application state, which is nothing more than a singleton-scoped dictionary.

We explore two examples: one about the application state, in case you were wondering where that feature disappeared to. Then, the Wishlist project also uses the singleton lifetime to provide an application-level feature. There are also a few unit tests to play with testability and to allow safe refactoring.

Project – Application state

You might remember the application state if you programmed ASP.NET using .NET Framework or the “good” old classic ASP with VBScript. If you don’t, the application state was a key/value dictionary that allowed you to store data globally in your application, shared between all sessions and requests. That is one...

Understanding guard clauses

A guard clause represents a condition the code must meet before executing a method. Essentially, it’s a type of code that “guards” against continuing the execution of the method if certain conditions aren’t met.

In most cases, guard clauses are implemented at the beginning of a method to throw an exception early when the conditions necessary for the method’s execution are not satisfied. Throwing an exception allows callers to catch the error without the need to implement a more complex communication mechanism.

We already stated that we use constructor injection to inject the required dependencies reliably. However, nothing fully guarantees us that the dependencies are not null. Ensuring a dependency is not null is one of the most common guard clauses, which is trivial to implement. For example, we could check for nulls in the controller by replacing the following:

_locationService = locationService;

With...

Understanding the Service Locator pattern

Service Locator is an anti-pattern that reverts the IoC principle to its Control Freak roots. The only difference is that you use the IoC container to build the dependency tree instead of the new keyword.

There is some use of this pattern in ASP.NET, and we may argue that there are some reasons for using the Service Locator pattern, but it should happen rarely or never in most applications. For that reason, let’s call the Service Locator pattern a code smell instead of an anti-pattern.

My strong recommendation is don’t use the Service Locator pattern unless you know you are not creating hidden coupling or have no other option.

As a rule of thumb, you want to avoid injecting an IServiceProvider in your application’s code base. Doing so reverts to the classic flow of control and defeats the purpose of DI.

A good use of Service Locator could be to migrate a legacy system that is too big to rewrite. So, you...

Revisiting the Factory pattern

A factory creates other objects; it is like a literal real-world factory. We explored in the previous chapter how to leverage the Abstract Factory pattern to create families of objects. A factory can be as simple as an interface with one or more Create[Object] methods or, even more, a simple delegate. We explore a DI-oriented simple factory in this section. We are building on top of the Strategy pattern example.

In that example, we coded two classes implementing the ILocationService interface. The composition root used the #define preprocessor directive to tell the compiler what bindings to compile. In this version, we want to choose the implementation at runtime.

Not compiling the code we don’t need is good for many reasons, including security (lowering the attack surface). In this case, we are simply using an alternative strategy useful for many scenarios.

To achieve our new goal, we can extract the construction logic...

Summary

This chapter delved into DI, understanding its crucial role in crafting adaptable systems. We learned how DI applies the IoC principle, shifting dependency creation from the objects to the composition root. We explored the IoC container’s role in object management, service resolution and injection, and dependency lifetime management. We tackled the Control Freak anti-pattern, advocating for DI over using the new keyword.

We revisited the Strategy pattern and explored how to use it with DI to compose complex object trees. We learned about the principle of composition over inheritance, which encourages us to inject dependencies into the classes instead of relying on base class features and inheritance. We explored different ways of injecting dependencies into objects, including constructor injection, property injection, and method injection.

We learned that a guard clause is a condition that must be met before a method is executed, often used to prevent null dependencies...

Questions

Let’s take a look at a few practice questions:

  1. What are the three DI lifetimes that we can assign to objects in ASP.NET Core?
  2. What is the composition root for?
  3. Is it true that we should avoid the new keyword when instantiating volatile dependencies?
  4. What is the pattern that we revisited in this chapter that helps compose objects to eliminate inheritance?
  5. Is the Service Locator pattern a design pattern, a code smell, or an anti-pattern?
  6. What is the principle of composition over inheritance?

Further reading

Here are some links to build upon what we have learned in the chapter:

Answers

  1. Transient, Scoped, Singleton.
  2. The composition root holds the code that describes how to compose the program’s object graph—the types bindings.
  3. Yes, it is true. Volatile dependencies should be injected instead of instantiated.
  4. The Strategy pattern.
  5. The Service Locator pattern is all three. It is a design pattern used by DI libraries internally but becomes a code smell in application code. If misused, it is an anti-pattern with the same drawbacks as using the new keyword directly.
  6. The principle of composition over inheritance encourages us to inject dependencies into classes and use them instead of relying on base class features and inheritance. This approach promotes flexibility and code reuse. It also negates the need for the LSP.

Learn more on Discord

To join the Discord community for this book – where you can share feedback, ask questions to the author, and learn about new releases – follow the...

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Architecting ASP.NET Core Applications - Third Edition
Published in: Mar 2024Publisher: PacktISBN-13: 9781805123385
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €14.99/month. Cancel anytime

Author (1)

author image
Carl-Hugo Marcotte

Carl-Hugo Marcotte is a software craftsman who has developed digital products professionally since 2005, while his coding journey started around 1989 for fun. He has a bachelor's degree in computer science. He has acquired a solid background in software architecture and expertise in ASP.NET Core through creating a wide range of web and cloud applications, from custom e-commerce websites to enterprise applications. He served many customers as an independent consultant, taught programming, and is now a Principal Architect at Export Development Canada. Passionate about C#, ASP.NET Core, AI, automation, and Cloud computing, he fosters collaboration and the open-source ethos, sharing his expertise with the tech community.
Read more about Carl-Hugo Marcotte