Minimal APIs in ASP.NET Core were designed to reduce ceremony and improve developer productivity. Combined with Scrutor, they enable teams to build clean, scalable applications with minimal boilerplate. However, this combination can also more easily hide Dependency Injection issues than traditional controller-based APIs.
Minimal APIs favor implicit dependency resolution, and Scrutor favors convention-based registration. When both are used together without discipline, small mistakes can turn into runtime bugs that are difficult to diagnose.
This article explores the most common Dependency Injection pitfalls developers face when using Scrutor with .NET Minimal APIs, explains the real-world problems they cause, and shows how to avoid them with clear, production-grade examples.
1. Over-Scanning Assemblies in Minimal APIs
Real-world problem
In Minimal APIs, all configuration often lives in Program.cs. Developers commonly add Scrutor early in development and use broad scanning rules to “just make it work”.
builder.Services.Scan(scan =>
scan.FromApplicationDependencies()
.AddClasses()
.AsImplementedInterfaces()
);
Why does this break in production
Registers unintended framework and third-party classes
Makes DI behavior unpredictable
Causes runtime failures when Minimal API endpoints resolve unexpected services
Slows application startup
Minimal APIs rely heavily on parameter-based injection. When the container contains unintended registrations, endpoints can silently resolve the wrong implementation.
Correct approach
Scan only assemblies you own and apply strict filters.
builder.Services.Scan(scan =>
scan.FromAssemblyOf<IApplicationService>()
.AddClasses(c => c.AssignableTo<IApplicationService>())
.AsImplementedInterfaces()
.WithScopedLifetime()
);
Minimal APIs benefit the most from precise and intentional DI registration.
2. Hidden Dependency Requirements in Minimal API Endpoints
Real-world problem
Minimal APIs make dependencies invisible because they appear directly in endpoint parameters.
app.MapGet("/orders", (IOrderService service) =>
{
return service.GetOrders();
});
With Scrutor, the registration logic is often far away from this endpoint.
Why this causes issues
Developers forget that endpoints are DI consumers
Missing registrations only fail at runtime
Refactoring interfaces can silently break endpoints
Correct approach
Always treat Minimal API endpoints as first-class DI consumers. Combine this with startup validation.
builder.Host.UseDefaultServiceProvider(options =>
{
options.ValidateScopes = true;
options.ValidateOnBuild = true;
});
This ensures missing registrations are caught early.
3. Accidental Multiple Interface Registrations
Real-world problem
A common pattern is tagging services with marker interfaces.
public class OrderService :
IOrderService,
IApplicationService,
IDisposable
{
}
Using Scrutor:
.AsImplementedInterfaces()
Why this is dangerous
This registers:
IOrderService
IApplicationService
IDisposable
Minimal API endpoints may accidentally resolve services through unintended interfaces, leading to incorrect behavior or runtime ambiguity.
Correct approach
Be explicit about which interface should be exposed.
.AddClasses(c => c.AssignableTo<IApplicationService>())
.As<IOrderService>()
.WithScopedLifetime()
4. Lifetime Mismatches in Minimal APIs
Real-world problem
Minimal APIs often look stateless, which tempts developers to use singletons.
services.AddSingleton<ReportService>();
public class ReportService
{
public ReportService(AppDbContext context) { }
}
Why this breaks under load
Minimal API endpoints are executed concurrently. A singleton capturing a scoped dependency like DbContext causes memory leaks, invalid state, and concurrency issues.
Correct approach
Align lifetimes correctly.
services.AddScoped<ReportService>();
Minimal APIs do not change DI lifetime rules. Scoped services are still the default for request-based logic.
5. Decorators That Hide Behavior in Minimal APIs
Real-world problem
Decorators are powerful but invisible at endpoint level.
services.Decorate<IOrderService, LoggingOrderService>();
services.Decorate<IOrderService, CachingOrderService>();
Endpoint code:
app.MapGet("/orders", (IOrderService service) =>
{
return service.GetOrders();
});
Why this is confusing
Endpoint code does not show logging or caching behavior
Decorator execution order is not obvious
Changing registration order changes runtime behavior
Correct approach
Register decorators together, document their order, and log decorator creation during development.
public CachingOrderService(...)
{
Console.WriteLine("Caching decorator created");
}
Decorator chains should be intentional and discoverable.
6. Mixing Manual and Scrutor Registrations
Real-world problem
Minimal API projects often grow organically.
services.AddScoped<IUserService, UserService>();
services.Scan(scan =>
scan.FromAssemblyOf<IApplicationService>()
.AddClasses(c => c.AssignableTo<IApplicationService>())
.AsImplementedInterfaces()
);
Why this causes bugs
The built-in DI container resolves the last registration. Small changes in registration order can change which implementation Minimal API endpoints receive.
Correct approach
Choose one registration strategy per layer. Use Scrutor consistently for application services and repositories, and manual registration only for infrastructure or framework services.
7. Service Locator Anti-Pattern in Minimal APIs
Real-world problem
Minimal APIs allow access to IServiceProvider , which tempts developers to resolve services manually.
app.MapGet("/users", (IServiceProvider provider) =>
{
var service = provider.GetRequiredService<IUserService>();
return service.GetAll();
});
Why this is harmful
Correct approach
Let Minimal APIs inject dependencies explicitly.
app.MapGet("/users", (IUserService service) =>
{
return service.GetAll();
});
This keeps endpoints readable and test-friendly.
8. Business Logic Inside Decorators
Real-world problem
Decorators are sometimes misused for authorization or business rules.
public class AuthorizationDecorator : IOrderService
{
public async Task CancelOrder(int id)
{
if (!IsAdmin())
throw new UnauthorizedAccessException();
await _inner.CancelOrder(id);
}
}
Why this is a design issue
Business rules become invisible at endpoint level
Behavior is harder to reason about
Violates single responsibility principles
Correct approach
Decorators should handle technical concerns such as logging, caching, metrics, or validation. Business rules should live in core services.
9. Missing DI Validation in Minimal APIs
Real-world problem
Minimal APIs often ship without DI validation because they “look simple”.
Why this causes outages
Missing registrations surface only under traffic
Lifetime mismatches fail randomly
Bugs reach production unnoticed
Correct approach
Always enable DI validation.
builder.Host.UseDefaultServiceProvider(options =>
{
options.ValidateScopes = true;
options.ValidateOnBuild = true;
});
This is especially important when using Scrutor.
Minimal APIs remove ceremony, and Scrutor removes boilerplate. Together, they also remove visibility. When something goes wrong, debugging becomes harder unless DI rules are strict and intentional.
Key Takeaway
Scrutor works exceptionally well with .NET Minimal APIs, but only when used with discipline. Tight assembly scanning, explicit lifetimes, validated startup configuration, and clear decorator usage are essential.
Minimal APIs do not reduce the importance of good Dependency Injection practices. In fact, they increase it.
Happy Coding!
I write about modern C#, .NET, and real-world development practices. Follow me on C# Corner for regular insights, tips, and deep dives.