Dependency Injection

IFinanceDbContext is defined in Application and implemented by FinanceDbContext in Infrastructure. DI connects the two, so Application never references the concrete class. Why the interface lives in Application is in Clean Architecture vs my design.

at startup each request request scope Program.cs AddInfrastructure() DI container FinanceDbContext scoped · UseNpgsql IFinanceDbContext maps to FinanceDbContext same instance for both keys ExpenseService IFinanceDbContext db injected FinanceDbContext one instance PostgreSQL queried only on ToListAsync() SaveChangesAsync()
Fig. 01  ·  Registered once at startup, resolved once per request

Infrastructure registers the DbContext

backend/src/Finance.Infrastructure/DependencyInjection.csservices.AddDbContext<FinanceDbContext>(options =>
    options.UseNpgsql(configuration.GetConnectionString("FinanceDb"))
);

services.AddScoped<IFinanceDbContext>(sp => sp.GetRequiredService<FinanceDbContext>());

return services;
  • AddDbContext() registers FinanceDbContext with a scoped lifetime and configures PostgreSQL using the FinanceDb connection string.
  • AddScoped() maps IFinanceDbContext to the registered FinanceDbContext. sp is the IServiceProvider, which resolves the same context instance within the request scope.
  • return services returns the IServiceCollection containing the registrations, not the actual instances.

The API layer adds them to the container

backend/src/Finance.Api/Program.csbuilder.Services.AddInfrastructure(builder.Configuration);

The API layer (Finance.Api) calls the Infrastructure registration method to add these dependencies to the application's DI container.

A service receives the interface

When ExpenseService requests IFinanceDbContext, DI supplies the configured FinanceDbContext. The database is accessed later when EF Core executes operations such as ToListAsync() or SaveChangesAsync().

Inspecting registered services

The following code can be added to backend/src/Finance.Api/Program.cs before builder.Build() to print registered service types and their lifetimes:

backend/src/Finance.Api/Program.csforeach (var service in builder.Services)
{
    Console.WriteLine($"Service: {service.ServiceType.Name}, " + $"Lifetime: {service.Lifetime}");
}

What to look for

FinanceDbContext should appear in the list as scoped.

Next How ExternalIdentityId works with Keycloak