Service Lifetime Dependency Injection In .NET Core

Spread the love

Dependency Injection (DI) is a foundation of modern .NET development, ensuring that code is more modular, testable, and maintainable. This blog will dive deep into Service Lifetime in Dependency Injection in .NET Core.  We’ll explore what it is, why it matters, and how to use it effectively in your .NET Core applications. We’ll also look at practical examples to solidify our understanding.

Introduction to Dependency Injection

Dependency Injection is a design pattern used to implement IoC (Inversion of Control), which allows the creation of dependent objects outside of a class and providing those objects to a class in different ways. It makes your code more flexible, easy to test, and maintainable. In .NET Core, DI is built into the framework so that we can use DI straightforwardly. 

What is Service Lifetime?

Service Lifetime defines how long a service instance is kept alive. .NET Core provides three main service lifetimes:

  1. Transient
  2. Scoped
  3. Singleton

1. Transient Service Lifetime

Transient services are created each time they are requested. This is useful for lightweight, stateless services that don’t need to maintain any state between calls. This service is ideal for services that perform a single operation or don’t maintain any data.  

2. Scoped Service Lifetime

Scoped services are created once per request. This is useful for maintaining the state within a single request but not across different requests. It is perfect for web applications where each request needs a fresh instance of the service.

3. Singleton Service Lifetime

Singleton services are created the first time they are requested and then reused for every subsequent request. This is useful for services that need to maintain state throughout the application’s lifetime. This service is ideal for heavy, stateful services that would be costly to create repeatedly.

Let’s understand this with an example. So here’s a detailed example demonstrating the real-time usage of Transient, Scoped, and Singleton services in a .NET Core web application project.

Real-Time Usage of Services in a .NET Core Web Application

Scenario: E-commerce Website

Suppose we are creating an e-commerce website. We’ll use different services with varying lifetimes to handle operations like logging, user session management, and order processing.

1. Transient Services: Order Processing

Use Case: Transient services are ideal for handling individual order processing, which is stateless and does not require maintaining data between requests.

Example: An order processing service that calculates the total cost of an order and applies discounts.

Implementation:

public interface IOrderProcessingService
{
    decimal CalculateTotal(Order order);
}

public class OrderProcessingService : IOrderProcessingService
{
    public decimal CalculateTotal(Order order)
    {
        // Calculate total cost with discounts
        return order.Items.Sum(item => item.Price * item.Quantity);
    }
}

Service Registration:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IOrderProcessingService, OrderProcessingService>();
    // Other service registrations
}

2. Scoped Services: Shopping Cart

Use Case: Scoped services are ideal for maintaining user-specific data during a single request, like a shopping cart that tracks items added by the user.

Example: A shopping cart service that stores items for the duration of a user’s session.

Implementation:

public interface IShoppingCartService
{
    void AddItem(Item item);
    IEnumerable<Item> GetItems();
}

public class ShoppingCartService : IShoppingCartService
{
    private readonly List<Item> _items = new List<Item>();

    public void AddItem(Item item)
    {
        _items.Add(item);
    }

    public IEnumerable<Item> GetItems()
    {
        return _items;
    }
}

Service Registration:

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IShoppingCartService, ShoppingCartService>();
    // Other service registrations
}

3. Singleton Services: Logging

Use Case: Singleton services are best for application-wide concerns like logging, where a single instance needs to be used throughout the application’s lifetime.

Example: A logging service that collects logs from different parts of the application and writes them to a file or database.

Implementation:

public interface ILoggerService
{
    void Log(string message);
}

public class LoggerService : ILoggerService
{
    private readonly string _logFilePath = "logs.txt";

    public void Log(string message)
    {
        File.AppendAllText(_logFilePath, $"{DateTime.Now}: {message}\n");
    }
}

Service Registration:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<ILoggerService, LoggerService>();
    // Other service registrations
}

Controller Example Using These Services

public class HomeController : Controller
{
    private readonly IOrderProcessingService _orderProcessingService;
    private readonly IShoppingCartService _shoppingCartService;
    private readonly ILoggerService _loggerService;

    public HomeController(
        IOrderProcessingService orderProcessingService,
        IShoppingCartService shoppingCartService,
        ILoggerService loggerService)
    {
        _orderProcessingService = orderProcessingService;
        _shoppingCartService = shoppingCartService;
        _loggerService = loggerService;
    }

    public IActionResult Index()
    {
        // Add an item to the shopping cart
        _shoppingCartService.AddItem(new Item { Name = "Laptop", Price = 999.99M, Quantity = 1 });
        
        // Log the addition of the item
        _loggerService.Log("Added Laptop to the shopping cart.");

        // Calculate the total order cost
        var totalCost = _orderProcessingService.CalculateTotal(new Order { Items =   _shoppingCartService.GetItems().ToList() });
        
        // Log the total cost calculation
        _loggerService.Log($"Total order cost calculated: {totalCost}");

        ViewBag.TotalCost = totalCost;

        return View();
    }
}

Razor View Example

@{
    ViewData["Title"] = "Home Page";
}

<div class="text-center">
    <h1 class="display-4">Welcome to the E-commerce Store</h1>
    <p>Total Cost: @ViewBag.TotalCost</p>
</div>

Summary

In this real-time web application example, we’ve implemented and used:

  • Transient Services: For order processing, ensuring a new instance is created for each order calculation.
  • Scoped Services: For managing the shopping cart, and maintaining state throughout a user’s session.
  • Singleton Services: For logging, ensuring a single instance handles all log entries throughout the application’s lifetime.

Conclusion

Understanding service lifetimes in .NET Core is essential for building efficient and maintainable applications. By appropriately using Transient, Scoped, and Singleton lifetimes, you can manage the lifecycle of your services effectively, ensuring that your application performs well and behaves as expected.

With this knowledge, you can make informed decisions about how to register and use services in your .NET Core applications, leading to cleaner, more modular code that’s easier to test and maintain. Happy coding!


Spread the love