Mid MVC

Explain built-in dependency injection in ASP.NET Core.?

ASP.NET Core has a built-in Dependency Injection (DI) container, which means you

don’t need third-party frameworks like Autofac or Ninject.

DI allows you to register classes (services) in a central place and then inject them

wherever they’re needed. This promotes loose coupling, testability, and maintainability.

Example:

public interface IEmailService

void Send(string to, string subject, string body);

Follow :

public class EmailService : IEmailService

public void Send(string to, string subject, string body)

=> Console.WriteLine($"Email sent to {to}");

Register it in Program.cs:

builder.Services.AddScoped<IEmailService, EmailService>();

Use it in a controller:

public class AccountController : Controller

private readonly IEmailService _emailService;

public AccountController(IEmailService emailService)

_emailService = emailService;

public IActionResult Register()

_emailService.Send("user@example.com", "Welcome!", "Thanks

for joining!");

return View();

🔁 2. What is the lifetime of services (Transient,

Scoped, Singleton)?

ASP.NET Core provides three lifetimes for services:

Lifetime Description Example Use Case

Follow :

Transien

A new instance is created every time

the service is requested.

Lightweight, stateless services like

formatters or mappers.

Scoped A new instance is created per HTTP

request.

Database contexts, unit of work

patterns.

Singleto

A single instance for the entire app

lifetime.

Configuration, caching, or logging

services.

Example:

builder.Services.AddTransient<IFormatter, TextFormatter>();

builder.Services.AddScoped<IProductRepository, ProductRepository>();

builder.Services.AddSingleton<IAppCache, MemoryAppCache>();

Tip: Scoped is most common for web apps — e.g., DbContext.

🧱 3. How do you inject dependencies into controllers?

ASP.NET Core automatically uses constructor injection for controllers.

The DI container will look at the constructor parameters and inject registered services.

Example:

public class OrderController : Controller

private readonly IOrderService _orderService;

public OrderController(IOrderService orderService)

_orderService = orderService;

public IActionResult Index() => View(_orderService.GetAll());

You don’t manually create OrderService; the framework does.

Follow :

🔄 4. How do you resolve dependencies in

middleware?

Middleware is created once at startup, so you can’t inject scoped services directly into its

constructor.

Instead, you resolve them inside the Invoke method using the

HttpContext.RequestServices.

Example:

public class LoggingMiddleware

private readonly RequestDelegate _next;

public LoggingMiddleware(RequestDelegate next) => _next = next;

public async Task InvokeAsync(HttpContext context)

var logger =

context.RequestServices.GetRequiredService<ILogger<LoggingMiddleware

>>();

logger.LogInformation("Request path: " +

context.Request.Path);

await _next(context);

Register it:

app.UseMiddleware<LoggingMiddleware>();

🧰 5. How to use IServiceProvider?

IServiceProvider is the built-in service resolver. You can use it to manually resolve

registered dependencies.

Follow :

Example:

var serviceProvider = builder.Services.BuildServiceProvider();

var emailService =

serviceProvider.GetRequiredService<IEmailService>();

emailService.Send("user@example.com", "Test", "DI working!");

In general:

  • Use IServiceProvider sparingly — prefer constructor injection.
  • Good for scenarios like custom factories or background tasks.

⚠ 6. What happens if a scoped service is injected into

a singleton?

That’s a lifetime mismatch — it causes problems because:

  • The singleton lives for the app’s lifetime.
  • The scoped service lives only for a single request.

If a singleton holds a reference to a scoped service, that scoped instance never gets

released — leading to memory leaks and unexpected data sharing across requests.

Solution:

  • Don’t inject scoped services into singletons.
  • Instead, inject an IServiceProvider and create a scope manually when needed:

public class ReportGenerator

private readonly IServiceProvider _provider;

Follow :

public ReportGenerator(IServiceProvider provider) => _provider =

provider;

public void Generate()

using var scope = _provider.CreateScope();

var db =

scope.ServiceProvider.GetRequiredService<AppDbContext>();

// Use db safely here

🧩 7. How to register multiple implementations for an

interface?

Sometimes you may have multiple implementations of the same interface — you can

register all of them and inject IEnumerable<T>.

Example:

builder.Services.AddTransient<INotificationService,

EmailNotification>();

builder.Services.AddTransient<INotificationService,

SmsNotification>();

Then:

public class NotificationManager

private readonly IEnumerable<INotificationService> _services;

public NotificationManager(IEnumerable<INotificationService>

services)

_services = services;

Follow :

public void NotifyAll(string message)

foreach (var service in _services)

service.Send(message);

This will call both EmailNotification and SmsNotification.

🧱 8. What is TryAdd in dependency injection?

TryAdd methods (from

Microsoft.Extensions.DependencyInjection.Extensions) register a service

only if it’s not already registered.

Example:

builder.Services.TryAddScoped<ILogService, DefaultLogService>();

If another part of the code already registered a custom ILogService, this one won’t

override it.

Use Case:

When writing reusable libraries or frameworks that should allow overriding defaults.

⚙ 9. How do you inject configuration or options into

services?

You can bind configuration sections (from appsettings.json) to strongly typed

classes and inject them using IOptions<T>.

Example:

// appsettings.json

Follow :

"SmtpSettings": {

"Host": "smtp.mailtrap.io",

"Port": 2525

Model:

public class SmtpSettings

public string Host { get; set; }

public int Port { get; set; }

Register:

builder.Services.Configure<SmtpSettings>(

builder.Configuration.GetSection("SmtpSettings"));

Use in service:

public class EmailService

private readonly SmtpSettings _settings;

public EmailService(IOptions<SmtpSettings> options)

_settings = options.Value;

🧠 10. What are IOptions, IOptionsSnapshot, and

IOptionsMonitor?

Interface Scope Use Case

Follow :

IOptions<T> Singleton Read config at startup — doesn’t change at

runtime.

IOptionsSnapshot<T

Scoped (per

request)

Re-reads configuration on each request.

Useful in web apps.

IOptionsMonitor<T> Singleton, listens

for changes

Automatically updates when config changes

(great for dynamic reloading).

Example:

public class MyService

private readonly IOptionsMonitor<SmtpSettings> _settings;

public MyService(IOptionsMonitor<SmtpSettings> settings)

_settings = settings;

public void SendMail()

Console.WriteLine($"Sending using host:

{_settings.CurrentValue.Host}");

If you change appsettings.json and

configuration reload is enabled,

IOptionsMonitor picks up the new

value without restarting the app.

Follow :

Entity Framework Core

More from ASP.NET Core MVC Tutorial

All questions for this course