skipnothing/.NET Platform

Keyed Services

Register `EmailMessageWriter` and `SmsMessageWriter` both as `IMessageWriter`, then ask for one, and the container hands back whichever you registered last. There was no built-in way to say which one you meant, until one extra field on the row.

  • Two registrations of one interface are indistinguishable until you add a key, then the same resolve can reach either.
  • A keyed row and a plain row of one type never answer each other's lookups; ask wrong and you throw.
  • The unit a resolve matches on widens from the service type to the type-and-key pair.
BUILDS ON
01

When Two Rows Share a Type

The container already lets you register two implementations of one service type. services.AddSingleton<IMessageWriter, EmailMessageWriter>() then services.AddSingleton<IMessageWriter, SmsMessageWriter>() appends two rows, both mapping IMessageWriter. The descriptor list keeps both, in registration order, exactly as the earlier topics showed.

Now resolve. GetRequiredService<IMessageWriter>() returns one object, and it is the last row you registered: SmsMessageWriter. The earlier row is still in the list, still constructible, but a single resolve has no way to reach it. Your only built-in handle on all of them is GetServices<IMessageWriter>(), which returns the whole set as an IEnumerable<IMessageWriter>.

So the list happily holds many rows for one type, but the two read paths are coarse: give me the last one, or give me all of them. Neither lets you say give me the email one. The rows are indistinguishable because they agree on the only thing resolution looks at, the service type. What is missing is a second thing to look at.

TWO IDENTICAL ROWS, LAST ONE WINS

Keep going, sign up to unlock the rest

4 more parts in this topic, plus 25+ more topics in .NET Platform.

Sign up, it's freeSee the full .NET Platform
Dependency Injection0/6#15 Registration Patterns
#17 DI Anti-Patterns