|
| 1 | +--- |
| 2 | +title: Dependency injection basics |
| 3 | +description: Learn how to use dependency injection (DI) in your .NET apps with this simple example. Follow along with this pragmatic guide to understand DI basics in C#. |
| 4 | +author: IEvangelist |
| 5 | +ms.author: dapine |
| 6 | +ms.date: 07/18/2024 |
| 7 | +no-loc: [Transient, Scoped, Singleton, Example] |
| 8 | +--- |
| 9 | + |
| 10 | +# Understand dependency injection basics in .NET |
| 11 | + |
| 12 | +In this article, you create a .NET console app that manually creates a <xref:Microsoft.Extensions.DependencyInjection.ServiceCollection> and corresponding <xref:Microsoft.Extensions.DependencyInjection.ServiceProvider>. You learn how to register services and resolve them using dependency injection (DI). This article uses the [Microsoft.Extensions.DependencyInjection](https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection) NuGet package to demonstrate the basics of DI in .NET. |
| 13 | + |
| 14 | +> [!NOTE] |
| 15 | +> This article doesn't take advantage of the [Generic Host](generic-host.md) features. For a more comprehensive guide, see [Use dependency injection in .NET](dependency-injection-usage.md). |
| 16 | +
|
| 17 | +## Get started |
| 18 | + |
| 19 | +To get started, create a new .NET console application named **DI.Basics**. Some of the most common approaches for creating a console project are referenced in the following list: |
| 20 | + |
| 21 | +- [Visual Studio: **File > New > Project**](/visualstudio/get-started/csharp/tutorial-console) menu. |
| 22 | +- [Visual Studio Code](https://code.visualstudio.com/) and the [C# Dev Kit extension's](https://code.visualstudio.com/docs/csharp/project-management): **Solution Explorer** menu option. |
| 23 | +- [.NET CLI: `dotnet new console`](/dotnet/core/tools/dotnet-new-sdk-templates#console) command in the terminal. |
| 24 | + |
| 25 | +You need to add the package reference to the [Microsoft.Extensions.DependencyInjection](https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection) in the project file. Regardless of the approach, ensure the project resembles the following XML of the _DI.Basics.csproj_ file: |
| 26 | + |
| 27 | +:::code language="XML" source="snippets/di/di-basics/di-basics.csproj"::: |
| 28 | + |
| 29 | +## Dependency injection basics |
| 30 | + |
| 31 | +Dependency injection is a design pattern that allows you to remove hard-coded dependencies and make your application more maintainable and testable. DI is a technique for achieving [Inversion of Control (IoC)](../../architecture/modern-web-apps-azure/architectural-principles.md#dependency-inversion) between classes and their dependencies. |
| 32 | + |
| 33 | +The abstractions for DI in .NET are defined in the [Microsoft.Extensions.DependencyInjection.Abstractions](https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection.Abstractions) NuGet package: |
| 34 | + |
| 35 | +- <xref:Microsoft.Extensions.DependencyInjection.IServiceCollection>: Defines a contract for a collection of service descriptors. |
| 36 | +- <xref:System.IServiceProvider>: Defines a mechanism for retrieving a service object. |
| 37 | +- <xref:Microsoft.Extensions.DependencyInjection.ServiceDescriptor>: Describes a service with its service type, implementation, and lifetime. |
| 38 | + |
| 39 | +In .NET, DI is managed by adding services and configuring them in an `IServiceCollection`. After services are registered, as `IServiceProvider` instance is built by calling the <xref:Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider%2A> method. The `IServiceProvider` acts as a container of all the registered services, and it's used to resolve services. |
| 40 | + |
| 41 | +## Create example services |
| 42 | + |
| 43 | +Not all services are created equally. Some services require a new instance each time that the service container gets them (_transient_), while others should be shared across requests (_scoped_) or for the entire lifetime of the app (_singleton_). For more information on service lifetimes, see [Service lifetimes](dependency-injection.md#service-lifetimes). |
| 44 | + |
| 45 | +Likewise, some services only expose a concrete type, while others are expressed as a contract between an interface and an implementation type. You create several variations of services to help demonstrate these concepts. |
| 46 | + |
| 47 | +Create a new C# file named _IConsole.cs_ and add the following code: |
| 48 | + |
| 49 | +:::code source="snippets/di/di-basics/IConsole.cs"::: |
| 50 | + |
| 51 | +This file defines an `IConsole` interface that exposes a single method, `WriteLine`. Next, create a new C# file named _DefaultConsole.cs_ and add the following code: |
| 52 | + |
| 53 | +:::code source="snippets/di/di-basics/DefaultConsole.cs"::: |
| 54 | + |
| 55 | +The preceding code represents the default implementation of the `IConsole` interface. The `WriteLine` method conditionally writes to the console based on the `IsEnabled` property. |
| 56 | + |
| 57 | +> [!TIP] |
| 58 | +> The naming of an implementation is a choice that your dev-team should agree on. The `Default` prefix is a common convention to indicate a _default_ implementation of an interface, but it's _not_ required. |
| 59 | +
|
| 60 | +Next, create an _IGreetingService.cs_ file and add the following C# code: |
| 61 | + |
| 62 | +:::code source="snippets/di/di-basics/IGreetingService.cs"::: |
| 63 | + |
| 64 | +Then add a new C# file named _DeafultGreetingService.cs_ and add the following code: |
| 65 | + |
| 66 | +:::code source="snippets/di/di-basics/DefaultGreetingService.cs"::: |
| 67 | + |
| 68 | +The preceding code represents the default implementation of the `IGreetingService` interface. The service implementation requires an `IConsole` as a primary constructor parameter. The `Greet` method: |
| 69 | + |
| 70 | +- Creates a `greeting` given the `name`. |
| 71 | +- Calls the `WriteLine` method on the `IConsole` instance. |
| 72 | +- Returns the `greeting` to the caller. |
| 73 | + |
| 74 | +The last service to create is the _FarewellService.cs_ file, add the following C# code before continuing: |
| 75 | + |
| 76 | +:::code source="snippets/di/di-basics/FarewellService.cs"::: |
| 77 | + |
| 78 | +The `FarewellService` represents a concrete type, not an interface. It should be declared as `public` to make it accessible to consumers. Unlike other service implementation types that were declared as `internal` and `sealed`, this code demonstrates that not all services need to be interfaces. It also shows that service implementations can be `sealed` to prevent inheritance and `internal` to restrict access to the assembly. |
| 79 | + |
| 80 | +## Update the `Program` class |
| 81 | + |
| 82 | +Open the _Program.cs_ file and replace the existing code with the following C# code: |
| 83 | + |
| 84 | +:::code source="snippets/di/di-basics/Program.cs"::: |
| 85 | + |
| 86 | +The preceding updated code demonstrates the how-to: |
| 87 | + |
| 88 | +- Create a new `ServiceCollection` instance. |
| 89 | +- Register and configure services in the `ServiceCollection`: |
| 90 | + - The `IConsole` using the implementation factory overload, return a `DefaultConsole` type with the `IsEnabled` set to `true. |
| 91 | + - The `IGreetingService` is added with a corresponding implementation type of `DefaultGreetingService` type. |
| 92 | + - The `FarewellService` is added as a concrete type. |
| 93 | +- Build the `ServiceProvider` from the `ServiceCollection`. |
| 94 | +- Resolve the `IGreetingService` and `FarewellService` services. |
| 95 | +- Use the resolved services to greet and say goodbye to a person named `David`. |
| 96 | + |
| 97 | +If you update the `IsEnabled` property of the `DefaultConsole` to `false`, the `Greet` and `SayGoodbye` methods omit writing to the resulting messages to console. A change like this, helps to demonstrate that the `IConsole` service is _injected_ into the `IGreetingService` and `FarewellService` services as a _dependency_ that influences that apps behavior. |
| 98 | + |
| 99 | +All of these services are registered as singletons, although for this sample, it works identically if they were registered as _transient_ or _scoped_ services. |
| 100 | + |
| 101 | +> [!IMPORTANT] |
| 102 | +> In this contrived example, the service lifetimes don't matter, but in a real-world application, you should carefully consider the lifetime of each service. |
| 103 | +
|
| 104 | +## Run the sample app |
| 105 | + |
| 106 | +To run the sample app, either press <kbd>F5</kbd> in Visual Studio, Visual Studio Code, or run the `dotnet run` command in the terminal. When the app completes, you should see the following output: |
| 107 | + |
| 108 | +```console |
| 109 | +Hello, David! |
| 110 | +Goodbye, David! |
| 111 | +``` |
| 112 | + |
| 113 | +### Service descriptors |
| 114 | + |
| 115 | +The most commonly used APIs for adding services to the `ServiceCollection` are lifetime-named generic extension methods, such as: |
| 116 | + |
| 117 | +- `AddSingleton<TService>` |
| 118 | +- `AddTransient<TService>` |
| 119 | +- `AddScoped<TService>` |
| 120 | + |
| 121 | +These methods are convenience methods that create a <xref:Microsoft.Extensions.DependencyInjection.ServiceDescriptor> instance and add it to the `ServiceCollection`. The `ServiceDescriptor` is a simple class that describes a service with its service type, implementation type, and lifetime. It can also desribe implementation factories and instances. |
| 122 | + |
| 123 | +For each of the services that you registered in the `ServiceCollection`, you could instead call the `Add` method with a `ServiceDescriptor` instance directly. Consider the following examples: |
| 124 | + |
| 125 | +:::code source="snippets/di/di-basics/Program.ServiceDescriptors.cs" id="console"::: |
| 126 | + |
| 127 | +The preceding code is equivalent to how the `IConsole` service was registered in the `ServiceCollection`. The `Add` method is used to add a `ServiceDescriptor` instance that describes the `IConsole` service. The static method `ServiceDescriptor.Describe` delegates to various `ServiceDescriptor` constructors. Consider the equivalent code for the `IGreetingService` service: |
| 128 | + |
| 129 | +:::code source="snippets/di/di-basics/Program.ServiceDescriptors.cs" id="greeting"::: |
| 130 | + |
| 131 | +The preceding code describes the `IGreetingService` service with its service type, implementation type, and lifetime. Finally, consider the equivalent code for the `FarewellService` service: |
| 132 | + |
| 133 | +:::code source="snippets/di/di-basics/Program.ServiceDescriptors.cs" id="farewell"::: |
| 134 | + |
| 135 | +The preceding code describes the concrete `FarewellService` type as both the service and implementation types. The service is registered as a singleton service. |
| 136 | + |
| 137 | +## See also |
| 138 | + |
| 139 | +- [.NET dependency injection](dependency-injection.md) |
| 140 | +- [Dependency injection guidelines](dependency-injection-guidelines.md) |
| 141 | +- [Dependency injection in ASP.NET Core](/aspnet/core/fundamentals/dependency-injection) |
0 commit comments