Skip to content

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:

  1. Current Thing - the Thing instance itself (if it implements the requested type)
  2. Current Thing's Functions - all functions attached to the current Thing
  3. Parent Hierarchy - walks up Thing.Parent chain, checking each Thing and its functions
  4. Additional Objects - adapter-level singletons (SharedResources, generic factories)
  5. 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