.NET Things: Pipeline Design Pattern

James Louie
Pragmatic Programming
4 min readFeb 24, 2019

The pipeline (or chain of responsibility) design pattern is a programming pattern that allows you to chain composable units of code together to create a series of steps that make up an operation. This is a very powerful design that allows developers to build modular add-on components that extend the ASP.NET framework. Learning how to apply it, and where you can apply it will dramatically reduce repeated code and help follow the DRY (Don’t Repeat Yourself) principle.

It’s Probably Already Working For You!

An example this design pattern used in WebApi when your endpoint responds with an exception page. What’s happening behinds the scenes is that your code includes an exception handling middleware that catches exceptions and translates that into an HTML page response.

In .NET Core you can see it is registered with IApplicationBuilder.UseDeveloperExceptionPage():

This is an example of a middleware. One example of how ASP.NET uses the pipeline design pattern during the request processing phase of their web server.

How Do Pipelines Work?

Pipeline consist of three main components:interface, handler, and pipeline manager.

Interface — Defines a shared interface that each handler will implement. One of the requirements on the pipeline interface is that it has a reference to the next reference to the next action in the sequence.

Handler — Concrete implementation of the interface that can do actions before calling the next handler, as well as do actions after the next handler has returned.

Pipeline Manager — This maintains the collection and order of the handlers to be executed for a specific interface. Actions are directed at the manager which then pushes the request through the handler pipeline.

A visual example of how the pipeline works for middleware:

Middleware Pipeline Diagram, Microsoft

For example, a middleware must follow this interface:

The RequestDelegate class contains a reference to the next call, so following the diagram, we can see where to put code that executes the logic before the next middleware is called, and where the logic for after can be called.

The interface for middleware is the requirement that you have a RequestDelegate reference in the constructor, as well as the public Task InvokeAsync(HttpContext) function.

It’s Everywhere!

Microsoft has built ASP.NET with this design pattern in mind, and you can probably find a lot of ASP.NET constructs can be extended in this way.

Middleware

We talked about middleware above, but formally middleware is a component that sits on the web server and interacts with requests and responses that the web server responds to.

Some good uses for this:

  • Routing
  • Authentication
  • Object Translation
  • Logging
  • Network Access and Security

MVC Filters

Filters are in the same vein as middleware, but they are a ASP.NET MVC construct. They are further down in the request processing pipeline, after MVC has initialized the context with additional information related to routing and action to proceed with in your application.

MVC Filter Diagram, Microsoft

This is useful for when I need to have the name of the controller, or action to be called. Additionally this gives you more fine grained control over when this step can be applied, as you can attach it to specific Controllers or even Controller functions (endpoints).

Check out an example of the interface and implementation:

For more information on MVC Filters, see full documentation.

HTTP Message Handlers

You can also extend the functionality of HttpClient implementations with this pattern, because SendAsync function is wrapped with pipeline manager that allows you to attach one or many DelegatingHandler to execute pre and post SendAsync tasks.

Check out an example of the interface and implementation:

HTTP message handlers are great for when you want to create some uniform behavior across system boundaries. You can add required headers to requests, change routing information, and add additional steps such as validation and logging.

For more information on HTTP message handlers, see full documentation.

Use Them!

The pipeline design pattern is a great way to extend the functionality of ASP.NET projects to conform to your organization’s design requirements. These pipeline interfaces allow you to identify repeated code behaviors and encapsulate that behavior in classes so that they not only can be re-used within individual applications, but especially helpful in micro service architectures where they can be applied across many applications.

Resources and Further Reading

--

--

James Louie
Pragmatic Programming

Developer looking to make the code a little more clean