Adapter Dependency Injection¶
This document describes the dependency injection (DI) system available to adapter authors. Understanding when and what dependencies are available is critical for correct adapter implementation.
Overview¶
The adapter framework uses a hierarchical DI system that resolves dependencies by traversing the Thing tree structure. Unlike traditional flat DI containers, resolution depends on the current context (which Thing is being created or used).
Resolution Order¶
When resolving a dependency, the system searches in this order:
- Current Thing - the Thing instance itself (if it implements the requested type)
- Current Thing's Functions - all functions attached to the current Thing
- Parent Hierarchy - walks up
Thing.Parentchain, checking each Thing and its functions - Additional Objects - adapter-level singletons (SharedResources, generic factories)
- Base Provider - fallback to root DI container (system-wide services like ILogger)
[Current Thing] ───► [Thing's Functions]
│
▼
[Parent Thing] ────► [Parent's Functions]
│
▼
[Grandparent...] ──► [...]
│
▼
[Additional Objects]
│
▼
[Base Provider]
Construction Context¶
Understanding the context during construction is critical.
Thing Construction¶
When a Thing is being constructed:
- Context is the parent Thing, not the Thing being created
- The Thing's own functions do not exist yet - they are created after the Thing
- Resolution searches parent hierarchy upward, additional objects, and base provider
Things access their own functions directly via the generated Functions property, not through DI.
Function Construction¶
When a Function is being constructed:
- Context is the owner Thing
- Other functions of the same Thing may or may not exist yet
- Do not depend on sibling functions via DI - creation order is not guaranteed
What Is Available Where¶
| Dependency Location | Thing Constructor | Function Constructor |
|---|---|---|
| Base provider (ILogger, etc.) | Yes | Yes |
| Additional objects (SharedResources, factories) | Yes | Yes |
| Parent Thing and its functions | Yes | Yes |
| Grandparent and above | Yes | Yes |
| Owner Thing | N/A | Yes |
| Sibling functions | N/A | Unreliable |
| Child Things | No | No |
Command Handlers¶
Command handlers are static methods that can receive dependencies via DI. Parameters after thing and command are resolved from the Thing's context:
public static async Task<DeviceCommandResult> OpenDoor(
MyDevice thing, // the Thing receiving the command
Access.Open command, // the command itself
Protocol protocol) // injected from DI
{
// handle command...
}
The same resolution rules apply as for Thing construction - parent hierarchy, additional objects, and base provider are searched.
Device-scoped dependencies (e.g., IPersistentSettings<T>) resolve to the same instance as the Thing has - the handler shares scope with the device.
Lifecycle Phases¶
| Phase | Available Dependencies |
|---|---|
| Adapter creation | System services only |
| SharedResource creation | System services |
| Thing/Function construction | Parent hierarchy, additional objects, base provider |
| Runtime | Full tree |
See Also¶
- things.md - Thing hierarchy
- functions.md - Function system
- persistent-storage.md - IPersistentSettings usage