Registration Patterns
Every service in your app entered through one line: `services.AddScoped<IOrderRepository, OrderRepository>()`. That line has four other shapes, and picking the wrong one is how a library's default silently wins over yours, or never does.
- ▸A factory delegate changes who builds an object, never how many copies the container keeps.
- ▸Register the same service type twice and a single inject quietly gets the last one.
- ▸One open generic line wires up every closed type you will ever resolve.
Before the container resolves anything, registration is pure bookkeeping. services.AddSingleton<IClock, SystemClock>() does not build a SystemClock. It appends one ServiceDescriptor to a List<ServiceDescriptor> the IServiceCollection wraps, and returns. Call Add ten times and you have a ten-row list, in the exact order you wrote the calls.
Each row holds three things: the service type you ask for, the lifetime, and one of three ways to produce the instance. A row can carry an implementation type the container will construct, an instance you already built, or a delegate it will call later. Every registration method in this topic is just a different way to fill in that one row.
That is the whole model, and it pays off twice. Resolution reads this list, so the order of your Add calls and the shape of each row decide what GetRequiredService hands back. And because it is an ordered list and not a map keyed by service type, two rows can share the same service type without overwriting each other. Hold that picture: an append-only list of rows. The rest of this topic is four ways to write a row, and two rules for reading them back.