.NET Dependency Injection. 2nd edition “

image Hello, Habitants! The dependency injection (DI) paradigm has become the de facto dominant paradigm on the .NET platform over the past decade and is now a must-learn for all .NET developers.

This is a revised and updated edition of the classic book, Dependency Injection in .NET. You will learn how to properly inject dependencies to remove the hard coupling between application components. Take a look at detailed examples and a basic understanding of the core libraries required for dependency injection in .NET and .NET Core.

In the book:

• Refactoring existing code into loosely coupled code
• DI methods that work with statically typed OO languages
• Integration with common .NET Framework
• Updated examples illustrating DI in .NET Core

Dependency Injection Antipatterns

In this chapter

  • Create tightly coupled code using the Control Freak anti-pattern.
  • Query class dependencies using the Service Locator antipattern.
  • Making the global availability dependency unstable when using the Ambient Context antipattern.
  • Enforcing a specific constructor signature using the Constrained Construction anti-pattern.

Many dishes are cooked in a skillet with butter. If the recipe has not yet been mastered, you can start heating the oil, turn away from the pan and read the recipe. But, as soon as the cutting of vegetables is completed, the oil will begin to burn. You might think that steaming oil means the pan is hot and ready to cook. This is a common misconception of inexperienced chefs. When the oil starts to burn, it also begins to decompose. This is called reaching the smoke point. Most burned oils taste bad, but that’s not all. They form harmful compounds and lose beneficial antioxidants.

The previous chapter provided a quick comparison of design patterns with recipes. The pattern provides a language that everyone understands that can be used to constructively discuss a complex concept.

When a concept (or rather, an implementation) is distorted, we end up with an anti-pattern.

DEFINITION

An antipattern is a common solution to a problem that has clear negative consequences, although along with it there are other available documented solutions that have proven effective.

Overheated smoky oil is a common culinary antipattern. This is a very common mistake. Many inexperienced chefs admit it, because they think that this is the way to do it, and the negative consequences are expressed in the fact that the food loses its taste and becomes harmful.

Antipatterns are, in a sense, a formalized way of describing common mistakes people make repeatedly. This chapter will discuss some of the most common anti-patterns associated with dependency injection. All of them in one form or another came across to us in the course of our professional activities, and we ourselves sometimes sinned by using them.

Antipatterns are often simple attempts to implement DI in an application. But due to their incomplete adherence to the fundamentals, DI implementations can turn into solutions that do more harm than good. Learning about these anti-patterns can give you an idea of ​​the pitfalls to avoid when implementing your first DI projects. But even with many years of experience with DI, you can easily make mistakes.

ATTENTION

This chapter differs from the other chapters because most of the code shown is an example of how not to do a DI implementation. Don’t try to repeat this in practice!

Fixes to antipatterns can be made by restructuring the code in the direction of one of the DI patterns presented in Chapter 4. How difficult it is to fix in any given case depends on the implementation. For each anti-pattern, some generalized recommendations will be provided for restructuring it in order to obtain the most suitable pattern.

NOTE

Since this topic is not the main one for this book, restructuring from DI anti-pattern to pattern will be considered only in this chapter. If you are interested in an in-depth study of the possibilities of processing existing applications in the direction of using DI technology, then a whole book is devoted to this: Feathers MC Working Effectively with Legacy Code. – Prentice Hall, 2004. Although it is not just about DI, it covers most of the concepts covered in this book.

Sometimes legacy code requires drastic measures to make it testable. This often means making changes over time to avoid accidentally crashing a previously running application. In some cases, an anti-pattern may be the most appropriate temporary solution. While this might be an improvement over the original code, it is important to note that this does not make it a pattern; there are other documented and repeatable solutions that have proven effective. The anti-patterns discussed in this chapter are summarized in Table. 5.1.

image

The rest of the chapter describes each anti-pattern, presenting them in order of importance. You can read the chapter sequentially or study only what is of interest, since the sections are not related to each other. If you don’t want to read everything in a row, then we advise you to refer to the corresponding sections about the “Dictator” and “Service Locator” antipatterns.

The most important is the constructor dependency injection pattern, and the Dictator appears as the most common anti-pattern. It greatly interferes with the use of any correct DI technology, therefore, before starting to study other anti-patterns, we suggest focusing on it. But the Service Locator antipattern poses the greatest danger, since it is often seen as a solution to the problem posed by the Dictator antipattern. It will be discussed in section 5.2.

5.1. Dictator antipattern

What is the opposite of inversion of control? Initially, the concept of “inversion of control” was invented to oppose the usual state of affairs, but we cannot talk about the anti-pattern of the “business as usual”. Instead, the term “Dictator” is used to describe a class that does not give up managing its volatile dependencies.

DEFINITION

The Dictator antipattern is always generated anywhere other than the root of the composition, where the code relies on an unstable dependency. It violates the Dependency Inversion Principle discussed in Section 3.1.2.

The “Dictator” antipattern is formed, for example, when creating a new instance of an unstable dependency using the new keyword. The implementation of the Dictator antipattern is shown in Listing 5.1.

image

Each time an unstable dependency is created, an implicit statement of intent to manage the lifecycle of the instance is obtained, and that no one else will be able to intercept this particular object. While the use of the new keyword on an unstable dependency is an example of problematic code, it does not apply to its use on a stable dependency.

NOTE

You shouldn’t think that the new keyword has suddenly become “illegal”, but you should refrain from using it for unstable dependencies outside the composition root. Also, don’t forget about static classes, which can also become unstable dependencies. While you will never use the new keyword on these classes, reliance on these classes causes the same problems.

The most obvious anti-pattern “Dictator” appears when no effort is made to introduce any abstractions into the code. You saw several relevant examples in Chapter 2 when Mary was building her e-commerce application (see section 2.1). DI is not attempted with this approach. But even where developers have heard something about DI technology and about the possibility of creating a layout, the “Dictator” anti-pattern often occurs in one variation or another.

In the following subsections, we will show several examples that resemble the code we have come across in practice. In each of the cases considered, the developers tried to improve the interfaces, but at the same time they did not properly understand the original intentions and motives.

5.1.1. Example: Dictator antipattern obtained by updating dependencies using the new keyword

Many developers have heard about the principle of programming for interfaces, but did not understand the deep meaning inherent in it. Trying to get things right or stick to best practices, they created code that didn’t make much sense. For example, Listing 3.9 showed a variant of ProductService that uses an instance of the IProductRepository interface to retrieve product offerings. Let’s recall what the corresponding code looked like:

public IEnumerable<DiscountedProduct> GetFeaturedProducts()
{
    return
        from product in this.repository.GetFeaturedProducts()
        select product.ApplyDiscountFor(this.userContext);
}

The important thing here is that the repository component variable is an abstraction. In Chapter 3, you saw how the repository field can be populated by constructor injection, but we’ve seen other, more primitive attempts. One of them is shown in Listing 5.2.

image

The repository field is declared through the IProductRepository interface, so any component in the ProductService class (such as GetFeaturedProducts) is programmed for the interface. While this all seems to be correct, it does little because the type will always be SqlProductRepository at runtime. There is no way to intercept or change the repository variable, unless you change and recompile the code itself. In addition, if you hard-code a variable so that it will always have a concrete type, then nothing good will come of its declaration as an abstraction. Direct dependency creation using the new keyword is one example of the Dictator anti-pattern.

Before moving on to the analysis and possible ways of solving the problems arising from the use of the “Dictator” anti-pattern, let us consider a few more examples to get a clearer idea of ​​the context and general features of unsuccessful attempts to use DI technology. In the following example, a bad solution is immediately visible. Most developers will try to refine their approach.

Authors

Steven van Deursen is a freelancer from the Netherlands with .NET development and design experience since 2002. He lives in Nijmegen, is involved in programming and makes a living from it. In addition to programming, Stephen is fond of martial arts, loves to eat and, of course, drink good whiskey.

Mark Seemann is a programmer, software designer and lecturer based in Copenhagen, Denmark. He has been involved in software development since 1995, and has been interested in TDD technology since 2003, including six years as a consultant, developer and designer at Microsoft. Mark is currently engaged in software development. He is interested in reading, drawing, playing the guitar, and enjoys good wine and gourmet food.

More details about the book can be found at website of the publishing house

Table of contents

Excerpt

For Habitants, a 25% discount on a coupon – .NET

Upon payment for the paper version of the book, an e-book is sent to the e-mail.

Similar Posts

Leave a Reply