Asp.Net Core Built In Dependency Injection Container Complete Guide

 Last Update:2025-06-23T00:00:00     .NET School AI Teacher - SELECT ANY TEXT TO EXPLANATION.    9 mins read      Difficulty-Level: beginner

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

  1. 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>();
      }
      
  2. 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>();
      
  3. 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;
          }
      }
      
  4. 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;
          }
      }
      
  5. 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>();
      });
      
  6. 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>>();
      

Advanced Features and Best Practices

  1. 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.
  2. 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.
  3. 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>();
      
  4. 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();
      }
      
  5. 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

🔔 Note: Select your programming language to check or run code at

💻 Run Code Compiler

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.

  1. Create the Service Interface

    Inside your project, create an Interfaces folder and add a file named ITimeService.cs.

    // Interfaces/ITimeService.cs
    namespace DependencyInjectionExample.Interfaces
    {
        public interface ITimeService
        {
            string GetCurrentTime();
        }
    }
    
  2. Implement the Service Interface

    Next, create a Services folder and add a file named TimeService.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");
            }
        }
    }
    
  3. Register the Service in the DI Container

    Open the Startup.cs file in the ASP.NET Core project and register TimeService as the implementation of ITimeService.

    // 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).
  4. Inject the Service into a Controller

    Modify the HomeController to use the ITimeService.

    // 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 });
            }
        }
    }
    
  5. 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.

  1. 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.

  1. Create a New Interface and Service

    Create a new interface IMessageService and its implementation GreetingMessageService.

    // 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!";
            }
        }
    }
    
  2. Register and Inject the New Service

    Register the GreetingMessageService in Startup.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 the HomeController 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 ...
        }
    }
    
  3. 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:

  1. We created two services, ITimeService and IMessageService, along with their implementations.
  2. We registered these services in the ConfigureServices method of Startup.cs.
  3. We injected these services into our HomeController using constructor injection.
  4. 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.

Further Reading

You May Like This Related .NET Topic

Login to post a comment.