Instance Manager (DI)

Read time 5 minutes Package: Nalix.Framework
On this page

Namespace: Nalix.Framework.Injection
Assembly: Nalix.Framework

InstanceManager (public sealed partial class InstanceManager : SingletonBase<InstanceManager>, IReportable) is Nalix’s high-performance, Native AOT-safe dependency injection registry. It combines a 2-slot MRU [ThreadStatic] L1 cache with RuntimeTypeHandle-keyed concurrent storage and compile-time activators generated by InstanceGenerator.

Resolution & Registration Flow

Architecture Diagram
flowchart TD
    Req(["GetExistingInstance<T>()"]) --> L1Valid{"ThreadStatic L1 Valid?"}
    L1Valid -->|Slot 0 Match| Ret0(["Return Slot 0 (Zero Alloc)"])
    L1Valid -->|Slot 1 Match| Swap["Swap Slot 1 -> Slot 0 (MRU)"] --> Ret1(["Return Instance"])
    L1Valid -->|Miss / Invalidated| Dict{"In _instanceCache?"}
    Dict -->|Yes| Pub["Publish to L1 Slot 0"] --> Ret1
    Dict -->|No| RetNull(["Return null"])

    GetOrCreate(["GetOrCreateInstance<T>(args)"]) --> HasArgs{"args.Length == 0?"}
    HasArgs -->|Yes| CheckType{"In _instanceCache?"}
    CheckType -->|Yes| RetCached(["Return Cached Instance"])
    CheckType -->|No| Create["Create via Pre-Registered [Injectable] Activator"]
    HasArgs -->|No| Locked{"Is Locked?"}
    Locked -->|Yes| ThrowLock["Throw InvalidOperationException"]
    Locked -->|No| CheckSig{"In _signatureInstanceCache?"}
    CheckSig -->|Yes| RetCached
    CheckSig -->|No| Create
    Create --> MapItf["Map Pre-Registered [Injectable(typeof(IService))] Interfaces"]
    MapItf --> Track["Track IDisposable & Auto-Register CoreTelemetryTarget IReportable"]
    Track --> RetCached

Source Mapping

  • src/Nalix.Framework/Injection/InstanceManager.cs
  • src/Nalix.Framework/Injection/InstanceManager.Activators.cs
  • src/Nalix.Framework/Injection/InstanceManager.Types.cs
  • src/Nalix.Framework/Injection/InstanceManager.Report.cs
  • analyzers/Nalix.Analyzers.Generators/InstanceGenerator.cs

Key Architectural Guarantees

  1. 2-Slot MRU [ThreadStatic] L1 Cache: GetExistingInstance<T>() checks two thread-local MRU slots (s_tsKey0/s_tsKey1) guarded by a monotonic invalidation counter (s_slotsInvalidated). After warm-up, resolving a service on the packet hot path requires zero allocations and zero locks.
  2. Compile-Time Interface Mapping ([Injectable]): Interface registrations are explicit and AOT-safe. Annotating a class with [Injectable(typeof(IMyService))] causes InstanceGenerator to call InstanceManager.RegisterServiceMapping(typeof(MyService), typeof(IMyService)) in the assembly’s [ModuleInitializer]. When Register(new MyService()) or GetOrCreateInstance<MyService>() is called, all mapped service interfaces are populated automatically.
  3. 100% Native AOT Activators: Dynamic creation (GetOrCreateInstance, CreateInstance, CreateInstanceWithInjection) uses factory delegates pre-registered via InstanceManager.RegisterActivator by InstanceGenerator. Attempting to dynamically instantiate a type not decorated with [Injectable] throws InvalidOperationException.
  4. Hard Cache Caps & Lockdown(): Both _instanceCache and _signatureInstanceCache enforce a cap of 4096 entries (MaxCachedInstances = 4096, MaxSignatureInstances = 4096). Calling Lockdown() after startup freezes the registry so subsequent Register<T>, RegisterForClassOnly<T>, RemoveInstance, Clear, GetOrCreateInstance(Type, args), or GetOrCreateInstance<T>(args) (when args.Length > 0) calls throw InvalidOperationException.
  5. Safe Disposable & Reportable Lifecycle: Replaced or removed IDisposable instances are disposed once (only if no other type handle still references the same instance), and built-in CoreTelemetryTarget IReportable services (ITaskManager, IConnectionHub, IBufferPoolManager, IObjectPoolManager, ISessionService, etc.) are automatically registered with ReportRegistry.Instance (custom IReportable services can register explicitly via ReportRegistry.Instance.Register(key, instance)).

Public API Reference

Registration & Security

MethodSignatureDescription
Register<T>public void Register<T>(T instance) where T : classRegisters or atomically replaces instance under typeof(T) and any interfaces mapped via [Injectable(typeof(IService))]. Tracks IDisposable and built-in CoreTelemetryTarget IReportable instances.
RegisterForClassOnly<T>public void RegisterForClassOnly<T>(T instance) where T : classAlias for Register<T>(instance).
RegisterActivatorpublic static void RegisterActivator(Type type, Func<object?[], object> factory)Registers a compile-time factory delegate (invoked automatically by InstanceGenerator).
RegisterServiceMappingpublic static void RegisterServiceMapping(Type concreteType, Type serviceType)Maps concreteType to serviceType (invoked automatically by InstanceGenerator).
Lockdownpublic void Lockdown()Freezes the manager state. Subsequent Register<T>, RegisterForClassOnly<T>, RemoveInstance, Clear(), GetOrCreateInstance(Type, args), or GetOrCreateInstance<T>(args) (when args.Length > 0) calls throw InvalidOperationException.

Resolution & Creation

MethodSignatureDescription
GetExistingInstance<T>public T? GetExistingInstance<T>() where T : classLock-free hot-path lookup via the 2-slot MRU [ThreadStatic] L1 cache and _instanceCache. Returns null if not registered.
GetOrCreateInstance<T>public T GetOrCreateInstance<T>(params object?[] args) where T : classReturns the cached T instance or creates and caches a new one using its [Injectable] activator (when args.Length > 0, delegates to GetOrCreateInstance(typeof(T), args) and enforces Lockdown()).
GetOrCreateInstancepublic object GetOrCreateInstance(Type type, params object?[] args)Non-generic resolution/creation by Type (uses ActivatorKey signature caching when args are provided; throws InvalidOperationException if Lockdown() is active).
CreateInstancepublic object CreateInstance(Type type, params object?[] args)Creates a new instance via its [Injectable] activator without caching it.
CreateInstanceWithInjection<T>public T CreateInstanceWithInjection<T>() where T : classCreates a new uncached instance of T by selecting the greediest public constructor whose parameters can all be resolved from _instanceCache.
CreateInstanceWithInjectionpublic object CreateInstanceWithInjection(Type type)Non-generic greedy constructor injection overload.
HasInstance<T>public bool HasInstance<T>()Returns true if typeof(T) is currently in _instanceCache.

Removal, Cleanup & Diagnostics

MemberSignatureDescription
CachedInstanceCountpublic int CachedInstanceCount { get; }Total number of cached instances across _instanceCache and _signatureInstanceCache.
RemoveInstancepublic bool RemoveInstance(Type type)Removes type (and its mapped interfaces and signature instances) and disposes the instance if no longer referenced.
Clearpublic void Clear(bool dispose = true)Clears all caches and invalidates L1 slots; disposes tracked IDisposable instances when dispose = true.
GenerateReportpublic string GenerateReport()Returns a formatted diagnostic table of cached instances, activators, and hit/creation counters.
WriteReportDatapublic void WriteReportData(Utf8JsonWriter writer)Writes structured JSON diagnostic metrics.

Usage Example

using Nalix.Abstractions.Injection;
using Nalix.Framework.Injection;

public interface IPlayerRepository
{
    bool Exists(ulong playerId);
}

[Injectable(typeof(IPlayerRepository))]
public sealed class InMemoryPlayerRepository : IPlayerRepository
{
    public bool Exists(ulong playerId) => playerId != 0;
}

// 1. Register at startup (populates both InMemoryPlayerRepository and IPlayerRepository)
InstanceManager.Instance.Register(new InMemoryPlayerRepository());

// 2. Freeze registrations after startup
InstanceManager.Instance.Lockdown();

// 3. Zero-allocation hot-path resolution
IPlayerRepository? repo = InstanceManager.Instance.GetExistingInstance<IPlayerRepository>();