Welcome to your first lesson on Dependency Injection. Given your experience in C#, you've likely encountered the patterns we'll be discussing, but our goal throughout this course is to move beyond just "making it work." We'll formalize the concepts, connect them to core software design principles, and build a deep, nuanced understanding that you can apply with confidence, especially within your WPF and MVVM projects.
We'll start at the very beginning, as you requested, to ensure we build on a solid foundation. This lesson focuses on the most fundamental building block: the dependency itself. By the end of our session, you'll be able to formally define what a dependency is and confidently identify dependencies within any C# class. This simple but crucial skill is the first step toward mastering the powerful techniques that follow.
What is a Dependency?
In software engineering, a dependency is an object that another object relies on to perform its function. The consuming object (often called the client or consumer) needs the dependency (often called the service) to do its job.
The most straightforward, and most problematic, way a class can acquire a dependency is by creating it directly. You see this anytime a class uses the new keyword to instantiate another class it needs.
Let's look at a canonical example from the official .NET documentation.
This article provides the foundational definition of a dependency. As you read, pay close attention to the relationship between the Worker class and the MessageWriter class.
Please read the introduction and focus on the two code blocks under the heading "A dependency is an object". Notice how the Worker class creates its own instance of MessageWriter. This is the classic sign of a direct dependency.
In this example, Worker needs to write messages, and it uses MessageWriter to do so. Because Worker is responsible for creating the MessageWriter instance with new MessageWriter(), we say that the MessageWriter class is a dependency of the Worker class. The Worker cannot function without it.
This pattern appears everywhere, including in the MVVM architecture you're using. Consider a ViewModel that needs to fetch data from a service.
Dependency Injection in a WPF MVVM Application
This article from the DevExpress community blog frames the concept within a WPF/MVVM context, which should be very familiar to you.
Read the section titled Which Issues can be Resolved by Dependency Injection?. The UserViewModel's relationship with MyDataService is conceptually identical to the Worker's relationship with MessageWriter from the previous article.
As you saw, the UserViewModel instantiates MyDataService directly. Therefore, MyDataService is a dependency of UserViewModel.
Take a look at this snippet from a hypothetical application:
public class OrderProcessingService
{
private readonly SmtpEmailService _emailService = new SmtpEmailService();
private readonly CustomerRepository _customerRepository = new CustomerRepository();
public void ProcessOrder(Order order)
{
// 1. Get customer details
Customer customer = _customerRepository.GetById(order.CustomerId);
// 2. Process the order logic...
Console.WriteLine($"Processing order {order.Id} for {customer.Name}");
// 3. Send a confirmation email
_emailService.Send(customer.Email, "Your order is processed!");
}
}
Based on the pattern we've just discussed, can you identify the dependencies of the OrderProcessingService class?
It has two:
SmtpEmailService: It's needed to send the confirmation email.CustomerRepository: It's needed to fetch customer data from a database.
In both cases, OrderProcessingService is creating these objects itself, which, as both articles hint, leads to a tightly-coupled design. We will explore exactly what "tight coupling" means and why it's a problem in our next lesson.
Visualizing Dependency Complexity
In these simple examples, we have one class depending on one or two others. In any real-world application, from a hardware integration service to a complex WPF desktop app, these relationships form a complex web. A single top-level operation can trigger a chain reaction through dozens of interdependent objects. This web is often called a dependency graph or object graph.
Managing this graph is the central challenge that Dependency Injection solves.

Imagine trying to manually create every single object in a graph like the one pictured above, ensuring each one gets the correct instances of its dependencies. It quickly becomes unmanageable.
What is Not a Dependency?
It's equally important to understand what does not qualify as a dependency in the context of DI. A common point of confusion is the distinction between a "service" (a dependency) and a "model" or "data object".
Dependencies are objects that provide behavior. They do things: IMessageWriter writes messages, IRepository fetches data, IEmailService sends emails.
Data objects, on the other hand, just hold state. They don't have behavior or dependencies of their own. Examples include DTOs (Data Transfer Objects), entities mapped to database tables, or simple models passed between layers. You typically create many instances of these, and they are not managed by a DI container.
The following video provides a concise and clear explanation of this distinction.
Dependency Injection in .NET Core (.NET 6)
In his video "Dependency Injection in .NET Core," Tim Corey gives a practical and direct explanation for why you don't register models in a DI container.
Please watch the segment from this section. He explains that models are simply data structures, not dependencies, and should be created as needed (e.g., new()) without involving the DI container.
This is a crucial rule of thumb: if the object's primary purpose is to hold data and it has no dependencies of its own (like Customer, Order, or Product), it is not a "dependency" we need to manage with DI. If its purpose is to provide a service or execute logic (like OrderProcessor, EmailService, or UserRepository), it is.
Conclusion
In this lesson, we established our foundational vocabulary. You now have a formal definition for the most important term in this course.
Here are the key takeaways:
- A dependency is an object that another class needs to perform its work. These are typically services that provide behavior.
- The most basic way to identify a dependency is to look for where a class uses the
newkeyword to instantiate another helper or service class. - In any non-trivial application, these dependencies form a complex dependency graph.
- Simple data objects (models) whose primary role is to hold state are not considered dependencies in the context of DI.
In our next lesson, we will build directly on this foundation. We will explore why the direct instantiation we saw today (new MyDataService()) is problematic by formally defining the concepts of tight and loose coupling. Understanding this problem is key to appreciating the elegance of the solution that Dependency Injection provides.