using Invercargill; using Invercargill.DataStructures; namespace Inversion { /** * Error domain for container-related errors. */ public errordomain ContainerError { /** * The requested type is not registered in the container. */ NOT_REGISTERED, /** * Cycle detected in injection */ CYCLE_DETECTED, /** * Resolved to a registration with an invalid lifecycle for the lifecycle of the scope */ ILLEGAL_LIFECYCLE_COMBINATION } /** * The inversion of control container that manages component registrations and lifecycles. * * The container is responsible for: * - Storing component registrations * - Managing singleton instances * - Creating scopes for scoped instance management * - Resolving dependencies * * Multiple registrations can be mapped to a single service type. When resolving, * you can get a single instance (any one) or all instances. * * Example usage: * {{{ * var container = new Container(); * * // Register a type with a factory delegate * container.register(typeof(MyService), (scope) => new MyService()) * .with_lifecycle(Lifecycle.SINGLETON); * * // Register a type as multiple interfaces * container.register(typeof(MyImplementation), (scope) => new MyImplementation()) * .as_type(typeof(IServiceA)) * .as_type(typeof(IServiceB)) * .with_lifecycle(Lifecycle.SCOPED) * .build(); * * // Register with a custom Factory instance * var factory = new MyCustomFactory(); * container.register_with_factory_instance(typeof(MyService), factory) * .as_type(typeof(IService)) * .build(); * }}} */ public class Container : Object { private Catalogue registrations; private Dictionary singletons; public Container() { this.registrations = new Catalogue(); this.singletons = new Dictionary(); register_singleton(s => this); } public Registration register_factory_type(Type implementation_type, Factory factory, Lifecycle lifecycle) { var registration = new Registration(implementation_type, factory, lifecycle); this.registrations.add(implementation_type, registration); // Add subsequent aliases added to this registration registration.alias_added.connect(t => { this.registrations.add(t, registration); }); return registration; } public Registration register_factory(Factory factory, Lifecycle lifecycle) { return register_factory_type(typeof(T), factory, lifecycle); } public Registration register_type(Type implementation_type, owned FactoryDelegate factory_func, Lifecycle lifecycle) { var delegate_factory = new DelegateFactory((owned)factory_func); return register_factory_type(implementation_type, delegate_factory, lifecycle); } public Registration register(owned FactoryDelegate factory_func, Lifecycle lifecycle) { return register_type(typeof(T), (owned)factory_func, lifecycle); } public Registration register_transient(owned FactoryDelegate factory_func) { return register((owned)factory_func, Lifecycle.TRANSIENT); } public Registration register_scoped(owned FactoryDelegate factory_func) { return register((owned)factory_func, Lifecycle.SCOPED); } public Registration register_singleton(owned FactoryDelegate factory_func) { return register((owned)factory_func, Lifecycle.SINGLETON); } public bool is_registered(Type type) { return this.registrations.has(type); } public Registration get_registration(Type service_type) throws ContainerError { try { return this.registrations.get_any(service_type); } catch (Error e) { throw new ContainerError.NOT_REGISTERED( "Type %s is not registered in the container: %s".printf(service_type.name(), e.message) ); } } /** * Gets all registrations for a service type. * * @param service_type The type to look up * @return An enumerable of all registrations for the type (may be empty) */ public Enumerable get_registrations(Type service_type) { return this.registrations.get_or_empty(service_type); } /** * Gets or creates a singleton instance for a registration. * * @param registration The registration to get the singleton for * @param scope The current scope for dependency resolution * @return The singleton instance * @throws Error if instance creation fails */ internal Object get_or_create_singleton(Registration registration, Type requested_type) throws Error { Object instance; if (!this.singletons.try_get(registration, out instance)) { var singleton_scope = new Scope(this, Lifecycle.SINGLETON); instance = create_with_injection_context(singleton_scope, requested_type, registration); this.singletons.set(registration, instance); } return instance; } /** * Creates a new scope for resolving scoped instances. * * @return A new Scope tied to this container */ public Scope create_scope() { return new Scope(this, Lifecycle.SCOPED); } public Scope create_transient_scope() { return new Scope(this, Lifecycle.TRANSIENT); } } }