ASP.NET Core includes that makes configured services available throughout an app. Services are added to the DI container with WebApplicationBuilder.Services.
ASP.NET Core provides a built-in service container, IServiceProvider, in which any can be registered.
Program.cs
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Data;
var builder = WebApplication.CreateBuilder(args);
var connectionStr = builder.Configuration.GetConnectionString("DefaultConnection");
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options =>
options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
var app = builder.Build();
Services are typically resolved from DI using constructor injection. The DI framework provides an instance of this service at runtime.
Injection of the service into the constructor of the class where it's used. The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.
public class IndexModel : PageModel {
private readonly RazorPagesMovieContext _context;
private readonly ILogger<IndexModel> _logger;
public IndexModel(RazorPagesMovieContext context, ILogger<IndexModel> logger) {
_context = context;
_logger = logger;
}
public IList<Movie> Movie { get;set; }
public async Task OnGetAsync() {
_logger.LogInformation("IndexModel OnGetAsync.");
Movie = await _context.Movie.ToListAsync();
}
}
In dependency injection terminology, a service:
Is typically an object that provides a service to other objects, such as the IMyDependency service.
Is not related to a web service, although the service may use a web service.
IServiceCollection - register services
IServiceProvider - resolve service instances
Service Lifetimes
Transient objects are always different.
Scoped objects are the same for a given request but differ across each new request.
Singleton objects are the same for every request.
The container calls Dispose() for the IDisposable types it creates. Services resolved from the container should never be disposed by the developer. If a type or factory is registered as a singleton, the container disposes the singleton automatically.
public class Service1 : IDisposable {
private bool _disposed;
public void Write(string message) {
Console.WriteLine($"Service1: {message}");
}
public void Dispose() {
if (_disposed)
return;
Console.WriteLine("Service1.Dispose");
_disposed = true;
}
}
public class Service2 : IDisposable {
private bool _disposed;
public void Write(string message) {
Console.WriteLine($"Service2: {message}");
}
public void Dispose() {
if (_disposed)
return;
Console.WriteLine("Service2.Dispose");
_disposed = true;
}
}
public interface IService3 {
public void Write(string message);
}
public class Service3 : IService3, IDisposable {
private bool _disposed;
public string MyKey { get; }
public Service3(string myKey) {
MyKey = myKey;
}
public void Write(string message) {
Console.WriteLine($"Service3: {message}, MyKey = {MyKey}");
}
public void Dispose() {
if (_disposed)
return;
Console.WriteLine("Service3.Dispose");
_disposed = true;
}
}
using DIsample2.Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddScoped<Service1>();
builder.Services.AddSingleton<Service2>();
var myKey = builder.Configuration["MyKey"];
builder.Services.AddSingleton<IService3>(sp => new Service3(myKey));
var app = builder.Build();
The service instances that aren't created by the service container, The framework doesn't dispose these services automatically. The developer is responsible for disposing the services.
High-level modules should not depend on low-level modules.
Abstraction should not depend upon details
Modules and Details should depend upon abstraction.
Benefits of Dependency Injection
promotes loose coupling of components
promotes logical abstraction of components
supports unit testing
How to find dependency?
Locate 'new' keyword usage
is the object a dependency?
apply dependency inversion
register the service
rinse and repeat
AddTransient
AddScoped
AddSingleton
instance of services are created each time they are requested
instance of services are created once per request
instance of services are created only once, the first time they were requested
Understanding Service Lifetime
Built-in IoC container manages the lifetime of a registered service type. It automatically disposes a service instance based on the specified lifetime.
The built-in IoC container supports three kinds of lifetimes:
Singleton: IoC container will create and share a single instance of a service throughout the application's lifetime.
or, once for the lifetime of the application.
Transient: The IoC container will create a new instance of the specified service type every time you ask for it.
or, each time they are requested.
Scoped: IoC container will create an instance of the specified service type once per request and will be shared in a single request.
or, once per request
Once we register a service, the IoC container automatically performs constructor injection if a service type is included as a parameter in a constructor.
Constructor Injection
public class HomeController : Controller
{
ILog _log;
public HomeController(ILog log)
{
_log = log;
}
public IActionResult Index()
{
_log.info("Executing /home/index");
return View();
}
}
Action Method Injection
Sometimes we may only need dependency service type in a single action method. For this, use [FromServices] attribute with the service type parameter in the method.
Action Method Injection
using Microsoft.AspNetCore.Mvc;
public class HomeController : Controller
{
public HomeController()
{
//some code
}
public IActionResult Index([FromServices] ILog log)
{
log.info("Index method executing");
return View();
}
}
Get Services Manually
It is not required to include dependency services in the constructor. We can access dependent services configured with built-in IoC container manually using RequestServices property of HttpContext
public class HomeController : Controller
{
public HomeController()
{
}
public IActionResult Index()
{
var services = this.HttpContext.RequestServices;
var log = (ILog)services.GetService(typeof(ILog));
log.info("Index method executing");
return View();
}
}