Dependency injection is in the framework, not a package

Coming from PHP, the surprising thing is not that the container exists — it is that there is exactly one, it ships with the framework, and every library targets it rather than shipping an adapter per container.

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IOrderRepository, SqlOrderRepository>();
    services.AddSingleton<IClock, SystemClock>();
    services.AddTransient<IEmailSender, SmtpSender>();

    services.Configure<StripeOptions>(Configuration.GetSection("Stripe"));
}

The three lifetimes are the whole API and they are the part worth getting right: transient is a new instance per resolution, scoped is one per request, singleton is one per process. Injecting a scoped service into a singleton is a captive dependency and the container throws at startup rather than handing you a stale connection at three in the morning — which is a check PHP containers generally do not perform. The deliberate limitation is that this container has no auto-registration by convention: every binding is a line, which is more typing than Symfony’s autowiring and is entirely explicit.