# Field Injection ## Overview Inversion provides field injection via `inject()` and `inject_all()` functions. These functions resolve dependencies during object construction when called within field initializers. The injection context is automatically established during container resolution. ## Namespace `Inversion` ## Injection Functions ### `inject() → T` Resolves a single dependency of type `T`. Returns `null` if resolution fails or no injection context exists. ```vala public class UserService : Object { private ILogger logger = inject(); public void greet(string name) { this.logger.log(@"Hello, $name!"); } } ``` ### `inject_all() → Lot` Resolves all registered implementations of type `T`. Returns empty collection if resolution fails. ```vala public class MultiHandler : Object { private Lot handlers = inject_all(); public void process_all(string request) { handlers.iterate((h) => h.handle(request)); } } ``` ### `try_inject() → T?` Attempts to resolve a single dependency. Returns `null` if no injection context exists or resolution fails. Use when dependency is optional. ```vala public class OptionalDeps : Object { private ICache? cache = try_inject(); // May be null } ``` ### `try_inject_all() → Lot` Attempts to resolve all implementations. Returns empty collection if no injection context exists. ```vala public class OptionalHandlers : Object { private Lot handlers = try_inject_all(); } ``` ## Injection Context ### How It Works 1. Container resolves a type via `Scope.resolve()` 2. `create_with_injection_context()` establishes thread-local context 3. Factory creates instance; field initializers execute 4. Field initializers call `inject()` which accesses current context 5. Context is cleaned up after instance creation ### `InjectionContext` Class Holds the current injection state: | Property | Type | Description | |----------|------|-------------| | `registration` | `Registration` | Current registration being resolved | | `requested_type` | `Type` | Type requested for resolution | | `scope` | `Scope` | Active scope for resolution | | `error` | `Error?` | Error if resolution failed | | `stack_registrations` | `HashSet` | Registrations in current stack | | `previous_context` | `InjectionContext?` | Parent context for nested resolutions | ### `get_injection_context() → InjectionContext` Gets the current injection context. Throws error if not initialized. ### `try_get_injection_context() → InjectionContext?` Gets current context or `null` if not available. ## Cycle Detection The injection context tracks the resolution stack to detect circular dependencies: ```vala // This will throw ContainerError.CYCLE_DETECTED public class CircularA : Object { private CircularB b = inject(); } public class CircularB : Object { private CircularA a = inject(); } ``` Error message includes the cycle path: ``` Cycle detected during dependency injection: CircularA => CircularB => CircularA ad infinitum. ``` ## Nested Injection Dependencies can have their own injected dependencies: ```vala public class ApiService : Object { private Controller controller = inject(); // Controller has its own injections } public class Controller : Object { private ILogger logger = inject(); private IRepository repo = inject(); } ``` ## Thread Safety Injection contexts are thread-local. Each thread has its own context stack, enabling concurrent resolution in different threads. ## Usage Patterns ### Required Dependencies Use `inject()` for required dependencies: ```vala public class OrderService : Object { private IOrderRepository orders = inject(); private IPaymentGateway payments = inject(); } ``` ### Optional Dependencies Use `try_inject()` for optional dependencies: ```vala public class CacheService : Object { private ICache? cache = try_inject(); public string? get(string key) { if (cache == null) return null; return cache.get(key); } } ``` ### Multiple Implementations Use `inject_all()` for plugin/handler patterns: ```vala public class ValidationService : Object { private Lot validators = inject_all(); public bool validate(Object obj) { foreach (var v in validators) { if (!v.validate(obj)) return false; } return true; } } ``` --- Field injection in Inversion uses `inject()` and `inject_all()` functions in field initializers to resolve dependencies automatically during container-managed object creation, with cycle detection and thread-local context management.