Chain of Responsibility Pattern: Passing Requests Like a Pro in .NET

Chain of Responsibility in .NET: Handle Requests Like a Pro

Learn how the Chain of Responsibility Pattern in .NET helps pass requests dynamically, decouple handlers, and enhance flexibility in your applications. 🚀


Introduction

Have you ever called customer support and had your request transferred to multiple departments before reaching the right person? 📞

Now, imagine handling authorization, logging, or request validation in your .NET application without tightly coupling components.

This is where the Chain of Responsibility (CoR) Pattern shines! 🌟

What You’ll Learn in This Article:

✔️ What is the Chain of Responsibility Pattern?
✔️ Why and when should you use it?
✔️ How to implement it in .NET?
✔️ Real-world use cases and best practices

By the end, you’ll be able to pass requests like a pro and write clean, maintainable code. Let’s dive in! 🚀


What is the Chain of Responsibility Pattern?

The Chain of Responsibility Pattern is a behavioral design pattern that allows a request to be processed by multiple handlers, one at a time, until the right handler takes action.

Key Benefits:

✔️ Decouples sender and receiver – Handlers don’t need to know who processes the request.
✔️ Enhances flexibility – Easily add/remove handlers without modifying the existing code.
✔️ Improves maintainability – Avoids complex if-else or switch statements.
✔️ Supports dynamic request processing – Different handlers can process requests based on conditions.


Real-World Analogy: IT Support Desk 💻

Imagine you contact IT support for an issue.

  • Level 1 Support tries to fix it.
  • If they can’t, they escalate to Level 2 Support.
  • If Level 2 can’t resolve it, it goes to Level 3 (Specialist Team).

Each level handles only the requests they can, passing others to the next level.

Similarly, in the Chain of Responsibility Pattern, requests move through a chain of handlers until the correct one processes them.


When to Use the Chain of Responsibility Pattern?

✅ When multiple objects can handle a request, but the exact handler isn’t known in advance.
✅ When you want to avoid large if-else or switch statements.
✅ When requests should be processed dynamically at runtime.
✅ When different handlers should process requests in a flexible sequence.


Implementing the Chain of Responsibility Pattern in .NET

Let’s build a real-world example: Processing customer complaints in a support system.

1️⃣ Define the Request Object

The request object contains details about the complaint.

public class SupportRequest
{
    public string Message { get; }
    public int Severity { get; } // 1 = Low, 2 = Medium, 3 = High

    public SupportRequest(string message, int severity)
    {
        Message = message;
        Severity = severity;
    }
}

2️⃣ Create an Abstract Handler Class

This class defines a template for processing requests and linking the next handler.

public abstract class SupportHandler
{
    protected SupportHandler _nextHandler;

    public void SetNextHandler(SupportHandler nextHandler)
    {
        _nextHandler = nextHandler;
    }

    public abstract void HandleRequest(SupportRequest request);
}

3️⃣ Implement Concrete Handlers

Each handler processes specific severity levels and passes others to the next handler.

Low-Level Support (Level 1)

public class Level1Support : SupportHandler
{
    public override void HandleRequest(SupportRequest request)
    {
        if (request.Severity == 1)
        {
            Console.WriteLine($"[Level 1 Support] Resolved: {request.Message}");
        }
        else if (_nextHandler != null)
        {
            _nextHandler.HandleRequest(request);
        }
    }
}

Medium-Level Support (Level 2)

public class Level2Support : SupportHandler
{
    public override void HandleRequest(SupportRequest request)
    {
        if (request.Severity == 2)
        {
            Console.WriteLine($"[Level 2 Support] Resolved: {request.Message}");
        }
        else if (_nextHandler != null)
        {
            _nextHandler.HandleRequest(request);
        }
    }
}

High-Level Support (Level 3 - Specialist Team)

public class Level3Support : SupportHandler
{
    public override void HandleRequest(SupportRequest request)
    {
        if (request.Severity == 3)
        {
            Console.WriteLine($"[Level 3 Support] Resolved: {request.Message}");
        }
        else
        {
            Console.WriteLine($"[Support System] No handler found for: {request.Message}");
        }
    }
}

4️⃣ Using the Chain of Responsibility Pattern

Now, let’s set up the chain and process requests.

class Program
{
    static void Main()
    {
        // Create handlers
        SupportHandler level1 = new Level1Support();
        SupportHandler level2 = new Level2Support();
        SupportHandler level3 = new Level3Support();

        // Chain the handlers
        level1.SetNextHandler(level2);
        level2.SetNextHandler(level3);

        // Create and process support requests
        SupportRequest request1 = new SupportRequest("Cannot connect to WiFi", 1);
        level1.HandleRequest(request1);

        SupportRequest request2 = new SupportRequest("Application crash on startup", 2);
        level1.HandleRequest(request2);

        SupportRequest request3 = new SupportRequest("Server down! Critical issue!", 3);
        level1.HandleRequest(request3);
    }
}

🔹 Expected Output:

[Level 1 Support] Resolved: Cannot connect to WiFi
[Level 2 Support] Resolved: Application crash on startup
[Level 3 Support] Resolved: Server down! Critical issue!

Key Takeaways:

✔️ Requests are passed dynamically without tightly coupling handlers.
✔️ Flexible chain allows adding new handlers easily.
✔️ Enhances maintainability by removing unnecessary conditionals.


Best Practices for Using the Chain of Responsibility Pattern

Keep handlers focused – Each handler should process a specific type of request.
Ensure termination conditions – Avoid infinite loops by ensuring a request eventually gets handled.
Use with Dependency Injection – Makes handler chaining more dynamic and configurable.
Combine with Logging & Monitoring – Useful for debugging request flow.


Real-World Use Cases of the Chain of Responsibility Pattern

✔️ Middleware in ASP.NET Core – Request processing pipeline (Authentication, CORS, Logging).
✔️ Event Handling in UI Applications – Passing events up a UI hierarchy.
✔️ Logging Frameworks – Logging messages at different levels (Info, Warning, Error).
✔️ Authorization Systems – Multi-level user access control.


Chain of Responsibility vs. Other Design Patterns

Feature Chain of Responsibility Decorator Pattern Mediator Pattern
Purpose Pass requests through handlers Extend object behavior dynamically Centralize communication between objects
Focus Dynamic request handling Adding new behavior Decoupling object interactions
Example Support ticket escalation Logging decorators Chatroom message passing

Conclusion

The Chain of Responsibility Pattern helps process requests dynamically, reduce dependencies, and improve maintainability.

Key Takeaways:

✔️ Eliminates tight coupling between sender and receiver.
✔️ Creates a flexible request processing pipeline.
✔️ Ideal for handling authentication, logging, and UI event propagation.

🚀 What’s Next?
💡 Try implementing a Logging Chain where each handler logs different levels of messages!

💬 Got questions? Drop them in the comments!


FAQs

1. How is the Chain of Responsibility Pattern different from the Mediator Pattern?

The Mediator Pattern centralizes communication, while the Chain of Responsibility passes requests sequentially through handlers.

2. Can I use the Chain of Responsibility Pattern with Dependency Injection?

Yes! You can dynamically configure the handler chain at runtime using DI containers.

🚀 Enjoyed this article? Share it with your developer community! 🚀

Sandip Mhaske

I’m a software developer exploring the depths of .NET, AWS, Angular, React, and digital entrepreneurship. Here, I decode complex problems, share insightful solutions, and navigate the evolving landscape of tech and finance.

Post a Comment

Previous Post Next Post