What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Registering a concrete class is valid; it does not, by itself, make code hard to test. The important question is what the consuming class requests: the concrete implementation or a contract that tests and other configurations can replace. In .NET and ASP.NET Core, the container can register either. Choose an interface when it represents a useful substitution boundary—not as a rule that every class must have one.
What does the registration choice change?
Dependency injection separates object construction and wiring from the class that consumes a dependency. An abstraction lets the consumer name a contract while the container supplies an implementation. Without that boundary, a consumer that requests a concrete class names that implementation in its constructor.
As an Amazon Associate I earn from qualifying purchases.
For example, these registrations expose different requested service types:
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →builder.Services.AddScoped<IMyDependency, MyDependency>();
builder.Services.AddSingleton<MyDependency>();
In the first registration, callers request IMyDependency, and the container supplies MyDependency. In the second, callers request MyDependency itself. ASP.NET Core also permits concrete-only registration; registering only an implementation type is equivalent to registering that same type as both the service type and implementation type. See Microsoft’s ASP.NET Core dependency-injection guidance.
The examples use different lifetimes—scoped and singleton—so they do not isolate the effect of the interface. When comparing designs, keep lifetime selection separate from the question of which type the consumer depends on.
Does injecting a concrete class make unit testing harder?
It can, when the real dependency is external, slow, costly, or otherwise difficult to isolate. If a consumer asks for MyDependency, a test that needs different behavior has fewer options: it may have to use the real class, change the consumer, or rely on other seams the design provides. If the consumer asks for IMyDependency, a test can supply another implementation that satisfies that contract.
That is a conditional benefit, not a guarantee that an interface automatically makes a test good or easy. The useful seam is the consumer’s dependency contract, and the registration tells the container which implementation to provide. Microsoft’s guidance puts the constructor-injection point plainly: “Requesting dependencies as constructor parameters yields classes that are easier to test.” Microsoft Learn offers this as design guidance; it does not quantify a testing-cost increase for concrete registration.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsWhen is an interface worth adding?
- A test needs a practical substitute: the real dependency performs I/O, has external effects, or makes a focused unit test impractical.
- Callers should not know the implementation: the implementation may change while the consumer-facing contract remains stable.
- Multiple implementations are meaningful: different runtime configurations or environments need distinct implementations behind the same contract.
In these cases, keep the contract focused on what its callers need. An interface is useful when it creates a real boundary, not merely because a class is registered in a DI container.
When is concrete injection reasonable?
Concrete injection can be a straightforward choice when the concrete class is the intended stable dependency and there is no meaningful alternate implementation or test seam to expose. Adding an interface solely to satisfy a blanket rule brings another abstraction to maintain without necessarily improving isolation.
Microsoft’s unit-testing guidance also cautions against indiscriminately passing interface implementations and emphasizes considering the context. There is no general measurement in the cited guidance for how much more a concrete dependency costs to test; the tradeoff depends on the dependency and the tests a project actually needs.
Rank #4
Registration is not the same as creating dependencies inside a class
A concrete type appearing in a container registration is not, by itself, the same design choice as a service directly instantiating its own dependencies. Microsoft recommends avoiding direct instantiation of dependent classes inside services because it couples the service to a particular implementation. Constructor injection keeps construction and wiring outside the consumer; whether that constructor requests a concrete type or an interface is a separate decision. See Microsoft’s .NET dependency-injection overview.
Quick Recap
Best Value
A practical decision check
- Inspect the consuming class’s constructor. Does it request
MyDependencyorIMyDependency? - Ask whether a test or runtime configuration needs to replace the dependency. If yes, identify the smallest useful contract the consumer needs.
- Check whether an alternate implementation is meaningful. If not, weigh the maintenance cost of an extra abstraction against a real substitution need.
- Choose the registration and lifetime independently. In ASP.NET Core, use the interface-backed or concrete-only registration pattern that matches the type requested by consumers, and select the lifetime for the service’s intended behavior.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




