.NET Core: Understanding of Scopes and Lifetime Management

.NET Core

Note. Before delving into this article, I recommend you read the previous part of this article for a better understanding. 

Short Note from the previous article.

  • Transient: Use for lightweight, stateless services that don't maintain any internal state. Each usage results in a new instance.
  • Scoped: Ideal for services that should share data and state within a single client request. Commonly used for database contexts and unit-of-work patterns.
  • Singleton: Suitable for services that maintain a shared state across the entire application. Be cautious with mutable states, as changes may affect other parts of your application.

Simple Image Illustration.

Scope and lifetime managemnet

Code to Reference,

Interface like below

namespace scopesandlifetimeservices.Services
{
    public interface IScopedService :IDisposable
    {
        public Guid ScopeId { get; set; }
    }

    public interface ISingletonService : IDisposable
    {
        public Guid SingletonId { get; set; }
    }

    public interface ITransientService : IDisposable
    {
        public Guid TransientId { get; set; }
    }
}

Implement the interfaces

namespace scopesandlifetimeservices.Services
{
    public class ScopedService : IScopedService
    {
        public Guid ScopeId { get; set; }

        public void Dispose() => GC.Collect();

        public ScopedService()
        {
            ScopeId= Guid.NewGuid();
        }
    }

    public class SingletonService : ISingletonService
    {
        public Guid SingletonId { get; set; }

        public void Dispose() => GC.Collect();
          
        public SingletonService()
        {
            SingletonId = Guid.NewGuid();
        }
    }

    public class TransientService : ITransientService
    {
        public Guid TransientId { get; set; }

        public void Dispose() => GC.Collect();

        public TransientService()
        {
            TransientId = Guid.NewGuid();
        }
    }
}

In Controller

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using scopesandlifetimeservices.Services;

namespace scopesandlifetimeservices.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class LifeTimeController : ControllerBase
    {
        readonly ISingletonService _singletonService;
        readonly IScopedService _scopedService;
        readonly ITransientService _transientService;
        readonly ISample _sample;

        public LifeTimeController(
            ISingletonService singletonService,
            IScopedService scopedService,
            ITransientService transientService,
            ISample sample)
        {
            _singletonService = singletonService;
            _scopedService = scopedService;
            _transientService = transientService;
            _sample = sample;
        }

        [HttpGet("request")]
        public ActionResult RequestMade()
        { 
            var message = $"<!DOCTYPE html><html lang=en><meta charset=UTF-8><meta content=\"width=device-width,initial-scale=1\"name=viewport><title>Scopes and Lifetime Management</title><h1>Scopes and Lifetime Management.</h1>" +
                $"GetSingletonID<ul><li><b>Sample Service </b>{_sample.GetSingletonID()}<li><b>ISingletonService Service</b> {_singletonService.SingletonId}</ul>" +
                $"GetScopedID<ul><li><b>Sample Service </b>{_sample.GetScopedID()}<li><b>IScopedService Service </b>{_scopedService.ScopeId}</ul>" +
                $"GetTransientID<ul><li><b>Sample Service </b>{_sample.GetTransientID()}<li><b>ITransientService Service</b> {_transientService.TransientId}</ul>";

            return new ContentResult
            {
                Content = message,
                ContentType = "text/html"
            };
        }
    }
}

In the Startup of the program.

builder.Services.AddSingleton<ISingletonService,SingletonService>();
builder.Services.AddScoped<IScopedService, ScopedService>();
builder.Services.AddTransient<ITransientService, TransientService>();

builder.Services.AddScoped<ISample, SampleDIConsumer>();

Response

Response

Conclusion

Choosing between Singleton, Scoped, and Transient depends on the specific requirements and usage patterns of your application. Singleton provides a shared instance across the entire application, Scoped offers sharing within a specific scope, and Transient ensures new instances are created whenever they are needed. Understanding these concepts is fundamental for designing scalable and maintainable software architectures.

In the upcoming blog, we'll explore running a sample application and uncovering the hidden treasures within .NET Core.