using System; using System.Collections.Generic; using System.Linq; namespace FlowScope.Container { public sealed class Container : IDisposable { [ThreadStatic] private static Stack _resolutionStack; private readonly Container _parent; private readonly Dictionary _registrations = new Dictionary(); private readonly List _children = new List(); private readonly List _ownedDisposables = new List(); private bool _disposed; public Container() { } private Container(Container parent) { _parent = parent; } public void RegisterInstance(T instance) { ThrowIfDisposed(); if (instance == null) { throw new ArgumentNullException(nameof(instance)); } RegisterLocal(typeof(T), ContainerRegistration.ForInstance(instance)); } public void RegisterFactory(Func factory) { RegisterSingletonFactory(factory); } public void RegisterSingletonFactory(Func factory) { ThrowIfDisposed(); if (factory == null) { throw new ArgumentNullException(nameof(factory)); } RegisterLocal(typeof(T), ContainerRegistration.ForSingletonFactory(this, c => factory(c))); } public void RegisterScoped(Func factory) { ThrowIfDisposed(); if (factory == null) { throw new ArgumentNullException(nameof(factory)); } RegisterLocal(typeof(T), ContainerRegistration.ForScopedFactory(this, c => factory(c))); } public void RegisterTransient(Func factory) { ThrowIfDisposed(); if (factory == null) { throw new ArgumentNullException(nameof(factory)); } RegisterLocal(typeof(T), ContainerRegistration.ForTransientFactory(this, c => factory(c))); } public void RegisterType() where TImplementation : TInterface { ThrowIfDisposed(); RegisterType(typeof(TInterface), typeof(TImplementation)); } public void RegisterType() { ThrowIfDisposed(); RegisterType(typeof(TImplementation), typeof(TImplementation)); } public void RegisterAssembly() { ThrowIfDisposed(); GeneratedFactories.RegisterAssembly(this); } public T Resolve() { ThrowIfDisposed(); return (T)Resolve(typeof(T)); } public bool TryResolve(out T value) { ThrowIfDisposed(); if (!TryFindRegistration(typeof(T), out var registration)) { value = default; return false; } value = (T)ResolveRegistration(typeof(T), registration); return true; } public Container CreateScope() { ThrowIfDisposed(); var child = new Container(this); _children.Add(child); return child; } public void Dispose() { if (_disposed) { return; } _disposed = true; var exceptions = new List(); for (var i = _children.Count - 1; i >= 0; i--) { TryDispose(_children[i], exceptions); } for (var i = _ownedDisposables.Count - 1; i >= 0; i--) { TryDispose(_ownedDisposables[i], exceptions); } _children.Clear(); _ownedDisposables.Clear(); ForgetScopedInstancesFor(this); _registrations.Clear(); if (_parent != null) { _parent._children.Remove(this); } if (exceptions.Count > 0) { throw new AggregateException("One or more container-owned instances failed to dispose.", exceptions); } } internal object Resolve(Type serviceType) { ThrowIfDisposed(); if (!TryFindRegistration(serviceType, out var registration)) { throw new InvalidOperationException($"No registration found for {FormatType(serviceType)}."); } return ResolveRegistration(serviceType, registration); } internal void RegisterFactory(Type serviceType, Func factory) { ThrowIfDisposed(); if (serviceType == null) { throw new ArgumentNullException(nameof(serviceType)); } if (factory == null) { throw new ArgumentNullException(nameof(factory)); } RegisterLocal(serviceType, ContainerRegistration.ForTransientFactory(this, factory)); } internal void TrackOwnedDisposable(IDisposable disposable) { _ownedDisposables.Add(disposable); } private void RegisterType(Type serviceType, Type implementationType) { if (!serviceType.IsAssignableFrom(implementationType)) { throw new InvalidOperationException($"{FormatType(implementationType)} cannot be assigned to {FormatType(serviceType)}."); } if (!GeneratedFactories.TryGetFactory(implementationType, out var factory)) { throw new InvalidOperationException( $"No generated factory registered for {FormatType(implementationType)}. Use GeneratedFactories.Register, ReflectionFactoryBuilder.RegisterType, or RegisterFactory before resolving this type."); } RegisterFactory(serviceType, factory); } private void RegisterLocal(Type serviceType, ContainerRegistration registration) { if (_registrations.ContainsKey(serviceType)) { throw new InvalidOperationException($"{FormatType(serviceType)} is already registered in this container scope."); } _registrations.Add(serviceType, registration); } private object ResolveRegistration(Type serviceType, ContainerRegistration registration) { _resolutionStack = _resolutionStack ?? new Stack(); if (_resolutionStack.Contains(serviceType)) { var chain = _resolutionStack.Reverse() .Concat(new[] { serviceType }) .Select(t => t.Name); throw new InvalidOperationException($"Circular dependency detected: {string.Join(" -> ", chain)}"); } _resolutionStack.Push(serviceType); try { return registration.Resolve(this); } finally { _resolutionStack.Pop(); if (_resolutionStack.Count == 0) { _resolutionStack = null; } } } private bool TryFindRegistration(Type serviceType, out ContainerRegistration registration) { if (_registrations.TryGetValue(serviceType, out registration)) { return true; } if (_parent != null) { return _parent.TryFindRegistration(serviceType, out registration); } return false; } private void ForgetScopedInstancesFor(Container scope) { foreach (var registration in _registrations.Values) { registration.ForgetScope(scope); } _parent?.ForgetScopedInstancesFor(scope); } private void ThrowIfDisposed() { if (_disposed) { throw new ObjectDisposedException(nameof(Container)); } } private static void TryDispose(IDisposable disposable, List exceptions) { try { disposable.Dispose(); } catch (Exception ex) { exceptions.Add(ex); } } private static string FormatType(Type type) => type.FullName ?? type.Name; } }