ASP.NET Core - Dependency Injection (DI)
ASP.NET Core is designed from the ground up to support Dependency Injection (DI). It uses a built-in Inversion of Control (IoC) container to automatically inject objects of dependency classes into constructors or methods.
Built-in IoC Container
ASP.NET Core provides a simple IoC container out-of-the-box. While it doesn't offer as many features as third-party IoC containers (such as auto-registration, scanning, interceptors, or decorators), it is efficient for most scenarios. If more advanced features are required, you can replace the built-in container with a third-party solution.
The built-in IoC container is implemented through the IServiceProvider
interface, which supports constructor injection by default. The types (classes) managed by the IoC container are called services.
Types of Services in ASP.NET Core
There are two main types of services in ASP.NET Core:
Framework Services: These are provided by the ASP.NET Core framework, such as
IApplicationBuilder
,IHostingEnvironment
, andILoggerFactory
.Application Services: These are custom services (types or classes) that you, as the developer, create for your application.
Before the IoC container can inject our application services, we need to register them with the container.
Registering Application Services
Let’s consider a simple example of an ILog
interface and its implementation, MyConsoleLogger
. We'll see how to register it with the IoC container and use it in an ASP.NET Core application.
To register this service, use the ConfigureServices
method in the Startup
class. This method accepts an IServiceCollection
parameter where services are registered.
Register Service Example:
In the example above:
The
Add
method ofIServiceCollection
registers a service with the IoC container.ServiceDescriptor
specifies the service type (ILog
) and its implementation (MyConsoleLogger
).By default, this registers the service as a singleton, meaning the same instance will be shared throughout the application's lifetime.
Once registered, ASP.NET Core will automatically inject an instance of MyConsoleLogger
wherever ILog
is requested in the constructor.
Understanding Service Lifetime
The IoC container manages the lifetime of registered services. It automatically disposes of a service based on the specified lifetime:
Singleton: A single instance is created and shared across the entire application's lifetime.
Transient: A new instance is created each time a service is requested.
Scoped: A new instance is created per request, and that instance is reused during the same request.
Registering Services with Different Lifetimes:
Alternatively, you can use the provided extension methods for a cleaner registration:
Using Extension Methods:
These extension methods provide a more straightforward approach for registering services with the desired lifetime.
Constructor Injection
Once a service is registered, the IoC container automatically performs constructor injection by providing the service type as a parameter in the constructor.
For example, let’s use the ILog
service in an MVC controller:
In this example, the IoC container automatically passes an instance of MyConsoleLogger
to the HomeController
constructor. The container will manage the instance based on the service lifetime (singleton, transient, or scoped).
Action Method Injection
Sometimes you may need the service only in a specific action method rather than throughout the controller. For this, you can use the [FromServices] attribute to inject the service directly into the action method.
Action Method Injection Example:
In this case, the IoC container will inject the ILog
service only when the Index
method is called.
Property Injection
The built-in IoC container does not support property injection. However, third-party IoC containers may offer this feature. For property injection, you would need to integrate a third-party container like Autofac.
Getting Services Manually
While it’s recommended to use constructor injection, sometimes you may want to manually access services from the IoC container. You can use the HttpContext.RequestServices
property to get services manually.
Example: Manual Service Resolution:
Although possible, this approach is discouraged because it can make the code harder to test and maintain. It's generally better to use constructor injection.
Conclusion
ASP.NET Core’s Dependency Injection system helps to manage service lifetimes and dependencies cleanly. The IoC container handles the creation and injection of services, reducing the need for tightly coupled code and making your application more maintainable and testable.
Use constructor injection whenever possible to let the IoC container automatically handle service dependencies.
Take advantage of the built-in IoC container for registering and managing your application services.
Be mindful of service lifetimes (singleton, transient, and scoped) to manage the lifespan of your services correctly.