Asp.Net Core Built In Dependency Injection Container Complete Guide
Understanding the Core Concepts of ASP.NET Core Built in Dependency Injection Container
Explaining the ASP.NET Core Built-In Dependency Injection Container
Understanding Dependency Injection
Dependency Injection is a design pattern that allows objects to receive their dependencies from an external source rather than constructing them themselves. This decoupling enhances application modularity, simplifies unit testing, and makes the architecture more flexible. In ASP.NET Core, the DI container automates this process, handling object instantiation and lifetime management based on defined service lifetimes.
Key Concepts of the ASP.NET Core DI Container
Service Registration:
- Services are registered with the DI container during the application startup phase, typically in the
Startup.cs
file. Registration involves specifying the service type, its implementation type, and the service lifetime.public void ConfigureServices(IServiceCollection services) { services.AddTransient<IWeatherForecastService, WeatherForecastService>(); services.AddScoped<IUserRepository, UserRepository>(); services.AddSingleton<ILogger, Logger>(); }
- Services are registered with the DI container during the application startup phase, typically in the
Service Lifetimes:
- Transient: Created every time they are requested from the container.
services.AddTransient<IWeatherForecastService, WeatherForecastService>();
- Scoped: Created once per client request (HTTP connection).
services.AddScoped<IUserRepository, UserRepository>();
- Singleton: Created once and reused throughout the application lifecycle.
services.AddSingleton<ILogger, Logger>();
- Transient: Created every time they are requested from the container.
Constructor Injection:
- Services are typically provided as constructor parameters to classes that depend on them. This promotes clarity and ensures that dependencies are always supplied.
public class WeatherForecastController : ControllerBase { private readonly IWeatherForecastService _weatherForecastService; public WeatherForecastController(IWeatherForecastService weatherForecastService) { _weatherForecastService = weatherForecastService; } }
- Services are typically provided as constructor parameters to classes that depend on them. This promotes clarity and ensures that dependencies are always supplied.
Property Injection and Method Injection:
- Although not recommended for service components, property and method injection can be used for specific scenarios such as optional dependencies.
public class UserController : ControllerBase { public IUserRepository UserRepository { get; set; } [ActivatorUtilitiesConstructor] public UserController(IUserDetailService userDetailService) { _userDetailService = userDetailService; } public void Configure(IUserRepository userRepository) { UserRepository = userRepository; } }
- Although not recommended for service components, property and method injection can be used for specific scenarios such as optional dependencies.
Factory Services:
- Complex dependencies can be handled using factory services, which provide the flexibility to create and configure object instances at runtime.
services.AddSingleton<Func<ILogger>>(serviceProvider => { return () => serviceProvider.GetService<ILogger>(); });
- Complex dependencies can be handled using factory services, which provide the flexibility to create and configure object instances at runtime.
Decorators:
- Decorators can be used to extend or modify the behavior of existing services without altering their underlying implementations.
services.AddTransient<IWeatherForecastService, WeatherForecastService>(); services.AddTransient<IWeatherForecastService, LoggingDecorator<WeatherForecastService>>();
- Decorators can be used to extend or modify the behavior of existing services without altering their underlying implementations.
Advanced Features and Best Practices
Transient vs. Singleton Considerations:
- Transient registration is suitable for stateless services, whereas Singleton registration is ideal for stateless, resource-intensive services with stable state that can be shared across the application.
Avoid Service Location:
- Service Location (resolving dependencies directly from the DI container) is generally avoided in favor of constructor injection to ensure clear dependency graphs and ease of testing.
Lazy Services:
- Lazy services can be registered to delay the creation of dependent services until they are actually required.
services.AddTransient<ILazyService>(provider => new LazyService(provider.GetService<IService>()))); services.AddTransient<ILogger, Logger>();
- Lazy services can be registered to delay the creation of dependent services until they are actually required.
Custom Modules:
- For large-scale applications, custom modules can be created to organize and encapsulate service registrations, promoting a modular application architecture.
public static class ServiceCollectionExtension { public static IServiceCollection RegisterCustomServices(this IServiceCollection services) { services.AddTransient<IService, Service>(); // Other service registrations return services; } } // Usage public void ConfigureServices(IServiceCollection services) { services.RegisterCustomServices(); }
- For large-scale applications, custom modules can be created to organize and encapsulate service registrations, promoting a modular application architecture.
Cross-Cutting Concerns:
- Cross-cutting concerns such as logging, exception handling, and caching are prime candidates for implementation via the DI container, enabling easier maintenance and testing.
Integration with Other DI Containers
While ASP.NET Core’s built-in DI container is robust, developers can choose to integrate third-party DI containers such as Autofac or Castle Windsor for additional features or performance benefits. Integration typically involves custom service provider configurations.
Conclusion
Online Code run
Step-by-Step Guide: How to Implement ASP.NET Core Built in Dependency Injection Container
Step 1: Setting Up a New ASP.NET Core Project
First, we need a new ASP.NET Core project. You can create this using Visual Studio or the .NET CLI. For simplicity, let’s use the .NET CLI:
dotnet new mvc -n DependencyInjectionExample
cd DependencyInjectionExample
Step 2: Understanding the DI Container
By default, ASP.NET Core comes with a simple built-in service container that supports constructor injection out of the box.
Constructor Injection Example
Let's create a simple ITimeService
interface and an implementation of it (TimeService
). Then we'll inject this service into a controller.
Create the Service Interface
Inside your project, create an
Interfaces
folder and add a file namedITimeService.cs
.// Interfaces/ITimeService.cs namespace DependencyInjectionExample.Interfaces { public interface ITimeService { string GetCurrentTime(); } }
Implement the Service Interface
Next, create a
Services
folder and add a file namedTimeService.cs
.// Services/TimeService.cs using DependencyInjectionExample.Interfaces; using System; namespace DependencyInjectionExample.Services { public class TimeService : ITimeService { public string GetCurrentTime() { return DateTime.Now.ToString("HH:mm:ss"); } } }
Register the Service in the DI Container
Open the
Startup.cs
file in the ASP.NET Core project and registerTimeService
as the implementation ofITimeService
.// Startup.cs using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using DependencyInjectionExample.Services; using DependencyInjectionExample.Interfaces; public class Startup { public void ConfigureServices(IServiceCollection services) { // Registering the TimeService services.AddTransient<ITimeService, TimeService>(); services.AddControllersWithViews(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); } }
Service Lifetime Options:
AddTransient
: Every time the service is requested, a new instance is created.AddSingleton
: One instance is created and reused for every request.AddScoped
: One instance per client request (scoped to the web request).
Inject the Service into a Controller
Modify the
HomeController
to use theITimeService
.// Controllers/HomeController.cs using Microsoft.AspNetCore.Mvc; using DependencyInjectionExample.Interfaces; namespace DependencyInjectionExample.Controllers { public class HomeController : Controller { private readonly ITimeService _timeService; public HomeController(ITimeService timeService) { _timeService = timeService; } public IActionResult Index() { ViewBag.CurrentTime = _timeService.GetCurrentTime(); return View(); } public IActionResult Privacy() { return View(); } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } } }
Display the Service Data in the View
Modify the
Index.cshtml
file to display the current time fetched from the service.<!-- Views/Home/Index.cshtml --> @{ ViewData["Title"] = "Home Page"; } <div class="text-center"> <h1 class="display-4">Welcome</h1> <p>The current time is @ViewBag.CurrentTime.</p> <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p> </div>
Step 3: Running the Application
Now that our service and controller are set up, let's run the application to see how the DI container works.
Run the Application
In the terminal, execute:
dotnet run
Navigate to
http://localhost:5000
(or whatever port was assigned).You should see a page displaying the current server time.
Step 4: Adding Additional Services
To further illustrate the DI container, let's add another service.
Create a New Interface and Service
Create a new interface
IMessageService
and its implementationGreetingMessageService
.// Interfaces/IMessageService.cs namespace DependencyInjectionExample.Interfaces { public interface IMessageService { string GetMessage(); } }
// Services/GreetingMessageService.cs using DependencyInjectionExample.Interfaces; namespace DependencyInjectionExample.Services { public class GreetingMessageService : IMessageService { public string GetMessage() { return "Hello from the DI container!"; } } }
Register and Inject the New Service
Register the
GreetingMessageService
inStartup.cs
.// Startup.cs using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using DependencyInjectionExample.Services; using DependencyInjectionExample.Interfaces; public class Startup { public void ConfigureServices(IServiceCollection services) { // Register the previously created services services.AddTransient<ITimeService, TimeService>(); // Register the new message service services.AddSingleton<IMessageService, GreetingMessageService>(); services.AddControllersWithViews(); } // ... other methods remain unchanged ... }
Now, inject
IMessageService
into theHomeController
and use it.// Controllers/HomeController.cs using Microsoft.AspNetCore.Mvc; using DependencyInjectionExample.Interfaces; namespace DependencyInjectionExample.Controllers { public class HomeController : Controller { private readonly ITimeService _timeService; private readonly IMessageService _messageService; public HomeController(ITimeService timeService, IMessageService messageService) { _timeService = timeService; _messageService = messageService; } public IActionResult Index() { ViewBag.CurrentTime = _timeService.GetCurrentTime(); ViewBag.Message = _messageService.GetMessage(); return View(); } // ... other methods remain unchanged ... } }
Display the New Service Data in the View
Update the
Index.cshtml
view to display the greeting message.<!-- Views/Home/Index.cshtml --> @{ ViewData["Title"] = "Home Page"; } <div class="text-center"> <h1 class="display-4">@ViewBag.Message</h1> <p>The current time is @ViewBag.CurrentTime.</p> <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p> </div>
Summary
In this example, we demonstrated how to use ASP.NET Core's built-in DI container:
- We created two services,
ITimeService
andIMessageService
, along with their implementations. - We registered these services in the
ConfigureServices
method ofStartup.cs
. - We injected these services into our
HomeController
using constructor injection. - We displayed the data from our services in the view.
By using this mechanism, you adhere to SOLID principles (especially Dependency Inversion Principle), making your code cleaner, more maintainable, and easier to test.
Login to post a comment.