HandlerRegistry
The HandlerRegistry is a flexible collection that stores conditional
handlers - pairs of predicates and actions that can be executed based on
runtime conditions. It’s particularly useful for implementing
chain-of-responsibility patterns, message routing, or conditional processing
workflows.
Overview
Each handler in the registry consists of:
CanHandle predicate: A
Predicate<T>that determines if the handler should process an itemHandler action: An
Action<T>that processes the item when the predicate matches
The registry supports both “first match” and “all matches” execution patterns, plus a stashing mechanism for temporarily switching handler sets.
Basic Usage
var registry = new HandlerRegistry();
// Register handlers with conditions
registry.Register<string>(
canHandle: s => s.StartsWith("ERROR"),
handler: s => Console.WriteLine($"Error logged: {s}")
);
registry.Register<string>(
canHandle: s => s.StartsWith("WARN"),
handler: s => Console.WriteLine($"Warning logged: {s}")
);
registry.Register<string>(
canHandle: s => s.Contains("URGENT"),
handler: s => SendAlert(s)
);
// Execute first matching handler
registry.Handle("ERROR: Database connection failed"); // Logs error
registry.Handle("URGENT WARN: System overload"); // Logs warning (first match)
// Execute all matching handlers
registry.HandleAll("URGENT WARN: System overload"); // Logs warning AND sends alert
Stashing Handlers
The stashing feature allows you to temporarily save the current handler set and work with a different set of handlers:
var registry = new HandlerRegistry();
// Register production handlers
registry.Register<string>(s => s.StartsWith("INFO"), s => LogToFile(s));
registry.Register<string>(s => s.StartsWith("ERROR"), s => LogToDatabase(s));
// Stash production handlers
registry.Stash();
// Register test handlers
registry.Register<string>(s => s.StartsWith("INFO"), s => LogToConsole(s));
registry.Register<string>(s => s.StartsWith("ERROR"), s => LogToConsole(s));
// Run tests with console logging
registry.Handle("INFO: Test message");
// Restore production handlers
registry.Pop();
// Back to file/database logging
registry.Handle("INFO: Production message");
The stash supports multiple levels - you can stash multiple times and pop them back in LIFO (Last In, First Out) order.
Common Use Cases
Message Processing
var messageRegistry = new HandlerRegistry();
messageRegistry.Register<Message>(
canHandle: msg => msg.Type == MessageType.Command,
handler: msg => commandProcessor.Process(msg)
);
messageRegistry.Register<Message>(
canHandle: msg => msg.Type == MessageType.Event,
handler: msg => eventStore.Save(msg)
);
HTTP Request Routing
var routeRegistry = new HandlerRegistry();
routeRegistry.Register<HttpRequest>(
canHandle: req => req.Path.StartsWith("/api/users"),
handler: req => userController.Handle(req)
);
routeRegistry.Register<HttpRequest>(
canHandle: req => req.Path.StartsWith("/api/orders"),
handler: req => orderController.Handle(req)
);
Validation Pipeline
var validatorRegistry = new HandlerRegistry();
validatorRegistry.Register<Order>(
canHandle: order => order.Amount > 1000,
handler: order => order.RequiresApproval = true
);
validatorRegistry.Register<Order>(
canHandle: order => order.Customer.IsVip,
handler: order => order.Priority = Priority.High
);