Resolving services
Once a container is declared, you ask it for services. This page covers the resolve surface. Most of the time you inject dependencies through constructors and never call resolve yourself, but the entry points matter at the edges of your app.
Relationships
A dependency does not have to be the service itself. It can be a way to get the service later, or more than once. These wrapper shapes are called relationships. A barista does not hold a hundred cups. It holds a way to make a cup when an order comes in.
Collections
When a service has several implementations, ask for a collection to get them all. Every syrup on the bar is an ISyrup. A consumer that wants the whole set asks for a collection of them.
Keyed dictionaries
A keyed dictionary hands you every keyed implementation of a service, mapped by its key. It is the milk fridge as a whole. Ask for the fridge and you can reach for oat, whole, or soy by name at runtime.
Property injection
Awaiten fills constructor parameters by default. Property injection is the opt-in alternative for the odd case where a constructor does not fit, like breaking a cycle between two services. It is done through an object initializer, so there is still no reflection.
Runtime arguments
Some values are only known when you resolve, not when you register. The size of a cup depends on what the customer ordered. A [Arg] parameter is filled from your call instead of the graph, and you supply it through a Func. Put the [Arg] on a container factory method to keep the produced type a plain class, or on the constructor directly when you would rather not add a factory.
Context-aware factories
Now and then a service should know who asked for it. The classic case is a logger named after the class that uses it. [RequestingType] gives a factory the consumer's type at each construction site.