using Inversion; /** * Example demonstrating scope-local registrations. * * This shows how to register items directly on a scope, * which is useful for per-request context in web applications. */ public class RequestContext : Object, IRequestContext { public string request_id { get; set; } public string path { get; set; } public RequestContext(string request_id, string path) { this.request_id = request_id; this.path = path; } } public interface IRequestContext : Object { public abstract string request_id { get; set; } public abstract string path { get; set; } } public class RequestLogger : Object { private RequestContext _context; public RequestLogger(RequestContext context) { this._context = context; } public void log(string message) { stdout.printf("[%s] %s\n", this._context.request_id, message); } } public class RequestHandler : Object { private RequestContext _context; private RequestLogger _logger; public RequestHandler(RequestContext context, RequestLogger logger) { this._context = context; this._logger = logger; } public void handle() { this._logger.log(@"Handling request for path: $(this._context.path)"); this._logger.log("Request completed successfully"); } } public class ApplicationService : Object { public void do_something() { stdout.printf("ApplicationService doing something...\n"); } } void main() { // Create the main container with application-wide services var container = new Container(); // Register application-wide singleton service container.register_singleton((scope) => { return new ApplicationService(); }); // Register handler as transient - it will receive scope-local dependencies container.register_transient((scope) => { var context = scope.resolve(); var logger = scope.resolve(); return new RequestHandler(context, logger); }); // Register logger as transient - it will receive scope-local context container.register_transient((scope) => { var context = scope.resolve(); return new RequestLogger(context); }); stdout.printf("=== Simulating Web Request 1 ===\n\n"); // Create a scope for the first request var scope1 = container.create_scope(); // Register request-specific context in the scope using register_local_scoped var context1 = new RequestContext("req-001", "/api/users"); scope1.register_local_scoped((s) => context1); // Resolve and use the handler - it gets the request-specific context var handler1 = scope1.resolve(); handler1.handle(); // Also resolve the application service - comes from container var app_service1 = scope1.resolve(); app_service1.do_something(); stdout.printf("\n=== Simulating Web Request 2 ===\n\n"); // Create a separate scope for the second request var scope2 = container.create_scope(); // Register a different context for this request var context2 = new RequestContext("req-002", "/api/products"); scope2.register_local_scoped((s) => context2); // This handler gets the second request's context var handler2 = scope2.resolve(); handler2.handle(); stdout.printf("\n=== Demonstrating Fluent API with as_type ===\n\n"); // Create a scope with an instance registered against an interface var scope3 = container.create_scope(); var context3 = new RequestContext("req-003", "/api/fluent"); // Use fluent API to register against multiple types scope3.register_local_scoped((s) => context3) .as_type(typeof(IRequestContext)); // Also register as interface // Can now resolve by either the concrete type or interface var by_concrete = scope3.resolve(); var by_interface = scope3.resolve(); stdout.printf("Resolved by concrete type: %s\n", by_concrete.request_id); stdout.printf("Resolved by interface: %s\n", by_interface.request_id); // Verify they're the same underlying object stdout.printf("Same instance: %s\n", ((Object)by_concrete) == ((Object)by_interface) ? "yes" : "no"); stdout.printf("\n=== Demonstrating Scope Isolation ===\n\n"); // Create a new scope - it does NOT inherit the other scope's local registrations var isolated_scope = container.create_scope(); // This would throw because RequestContext is not registered in isolated scope try { var handler_isolated = isolated_scope.resolve(); } catch (Error e) { stdout.printf("Expected error in isolated scope: %s\n", e.message); } stdout.printf("\n=== Demonstrating resolve_all with Both Sources ===\n\n"); // Register an additional RequestContext in scope2 using transient scope2.register_local_transient((s) => { return new RequestContext("req-002-secondary", "/api/products/secondary"); }); // resolve_all returns both scope-local and container registrations var all_contexts = scope2.resolve_all(); stdout.printf("All RequestContext instances:\n"); foreach (var obj in all_contexts.to_immutable_buffer()) { if (obj != null) { var ctx = (RequestContext) obj; stdout.printf(" - %s: %s\n", ctx.request_id, ctx.path); } } stdout.printf("\n=== Scope-Local Scoped Factory Demo ===\n\n"); var scope4 = container.create_scope(); // Register a scoped factory - same instance returned within the scope scope4.register_local_scoped((s) => { stdout.printf("Creating scoped RequestContext...\n"); return new RequestContext("req-004", "/api/scoped"); }); // First resolution creates the instance var ctx4a = scope4.resolve(); stdout.printf("First resolution: %s\n", ctx4a.request_id); // Second resolution returns the cached instance var ctx4b = scope4.resolve(); stdout.printf("Second resolution: %s\n", ctx4b.request_id); // Verify they're the same instance stdout.printf("Same instance: %s\n", ctx4a == ctx4b ? "yes" : "no"); stdout.printf("\nDone!\n"); }