In this article, let’s try to see how the default order of execution can be changed using IOrderedFilter interface.
Default Execution Order
We already have seen that filters can be configured for three different scopes – global scope, controller scope or on individual action.
When there are multiple filters at the same filter stage (authorization, resource, action, exeception and result filters) configured at different scope levels, they are executed in a specific order.
Filters from global scope wrap the filters specified on controller and filters specified on a controller wrap the filters specified on an action. This is the default order of execution.
So, let’s say there are action filters specified at all 3 scopes then below would be order of execution for action filter’s stage:
- Before method of global filter
- Before method of controller filter
- Before method of action level filter
- Action execution
- After method of action filter
- After method of controller filter
- After method of global filter
Changing Default Order
.NET Core provides an interface IOrderedFilter, which provides Order property. This property accepts an integer value.
A filter with lower order wraps all the other filters with higher order. Lower order filter’s Before method would execute before higher order filter and lower order filter’s After method would execute after the higher order filter.
So for example, let’s say there are two an action filters, one with order 0 and other with order 1. Then the OnActionExecuting of filter with order 0 would execute before OnActionExecuting of filter with order 1. And the OnActionExeuted of filter with order 0 would execute after OnActionExecuted of filter with order 1.
But where is the scope in this ? As per documentation, all the filters are first sorted by its order and then ties are resolved by the scope at which filter was specified.
All the built-in filters have order property and it is set to its default value, i.e. 0. Hence if Order property is not specified, higher scope filters wrap the lower scope filters.
Now is the moment of truth. Let’s see how it all works in action.
So, first, let’s create a simple custom action filter from ActionFilterAttribute. This declares a Scope property which is defaulted to value “Global“. This filter attribute writes output to debug console, outputting method name and Scope property’s value. This would help us understand the order in which this action filter attribute was executed.
The below code also shows startup configuration which adds this filter on a global scope. Now this attribute can also be specified at controller and action. On controller, set Scope property to “Controller” and on action, set Scope property to “Action“. Initially do not set Order property and run it.
Now, let’s play with Order property. Let’s set Order on action level to be 1 and on controller, set it to be 2. In that case the output would be:
- OnActionExecuting: Global
- OnActionExecuting: Action
- OnActionExecuting: Controller
- Action is executed
- OnActionExecuted: Controller
- OnActionExecuted: Action
- OnActionExecuted: Global
Every controller provides Controller.OnActionExecuting, Controller.OnActionExecutionAsync, and Controller.OnActionExecuted. These methods wrap all the action filters that run for particular action. And these methods cannot be set to run after filters applied to action.
I hope you find this example useful to understand the concept of ordered filters. Let me know your thoughts.