IMPLEMENTATION OF IActionFilter FILTER RUSSIAN DOLL IN MVC

Posted by sweatje on Fri, 09 Aug 2019 13:00:20 +0200

Looking at the source code of mvc, we know that it executes the InvokeAction method in the Controller Action Invoker class to implement the filter and action method execution.  

By looking at the source code, we know that he implemented the IActionFilter filter and action method by calling InvokeAction Method WithFilters, as shown in the figure.

Point in this way we can see.

1 protected virtual ActionExecutedContext InvokeActionMethodWithFilters(ControllerContext controllerContext, IList<IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters)
2 {
3     ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters);
4     Func<ActionExecutedContext> seed = () => new ActionExecutedContext(controllerContext, actionDescriptor, false, null)
5     {
6         Result = this.InvokeActionMethod(controllerContext, actionDescriptor, parameters)
7     };
8     return filters.Reverse<IActionFilter>().Aggregate(seed, (Func<ActionExecutedContext> next, IActionFilter filter) => () => ControllerActionInvoker.InvokeActionMethodFilter(filter, preContext, next))();
9 }

See here I am directly confused, because it delegates nested delegates and abbreviated, but also called the extension method Aggregate accumulator, so it is difficult to directly understand, this is how to execute the code in the end? Let me sort out the code as follows.

 1    public class Class1
 2     {
 3 
 4         protected virtual ActionExecutedContext InvokeActionMethodWithFilters(ControllerContext controllerContext, IList<IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters)
 5         {
 6             ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters);
 7 
 8             Func<ActionExecutedContext> seed = () => new ActionExecutedContext(controllerContext, actionDescriptor, false, null)
 9             {
10                 Result = this.InvokeActionMethod(controllerContext, actionDescriptor, parameters)
11             };
12 
13             Func<Func<ActionExecutedContext>, IActionFilter, Func<ActionExecutedContext>> secondParam =
14                 (Func<ActionExecutedContext> next, IActionFilter filter) =>
15                 {
16                     Func<ActionExecutedContext> returnFunc = () =>
17                     {
18                         return Class1.InvokeActionMethodFilter(filter, preContext, next);
19                     };
20 
21                     return returnFunc;
22 
23                     //This is abbreviation.
24                     //return () => Class1.InvokeActionMethodFilter(filter, preContext, next);                    
25                 };
26 
27             return filters.Reverse<IActionFilter>().Aggregate(seed,
28                 //(Func<ActionExecutedContext> next, IActionFilter filter) => () => Class1.InvokeActionMethodFilter(filter, preContext, next)
29                 secondParam
30                 )
31                 .Invoke();
32         }
33 
34         internal static ActionExecutedContext InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func<ActionExecutedContext> continuation)
35         {
36             filter.OnActionExecuting(preContext);
37             if (preContext.Result != null)
38             {
39                 return new ActionExecutedContext(preContext, preContext.ActionDescriptor, true, null)
40                 {
41                     Result = preContext.Result
42                 };
43             }
44             bool flag = false;
45             ActionExecutedContext actionExecutedContext = null;
46             try
47             {
48                 actionExecutedContext = continuation();
49             }
50             catch (ThreadAbortException)
51             {
52                 actionExecutedContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false, null);
53                 filter.OnActionExecuted(actionExecutedContext);
54                 throw;
55             }
56             catch (Exception exception)
57             {
58                 flag = true;
59                 actionExecutedContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false, exception);
60                 filter.OnActionExecuted(actionExecutedContext);
61                 if (!actionExecutedContext.ExceptionHandled)
62                 {
63                     throw;
64                 }
65             }
66             if (!flag)
67             {
68                 filter.OnActionExecuted(actionExecutedContext);
69             }
70             return actionExecutedContext;
71         }
72 
73         protected virtual ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters)
74         {
75             object actionReturnValue = actionDescriptor.Execute(controllerContext, parameters);
76             return this.CreateActionResult(controllerContext, actionDescriptor, actionReturnValue);
77         }
78 
79         protected virtual ActionResult CreateActionResult(ControllerContext controllerContext, ActionDescriptor actionDescriptor, object actionReturnValue)
80         {
81             if (actionReturnValue == null)
82             {
83                 return new EmptyResult();
84             }
85             ActionResult arg_29_0;
86             if ((arg_29_0 = (actionReturnValue as ActionResult)) == null)
87             {
88                 arg_29_0 = new ContentResult();
89                 //(arg_29_0 = new ContentResult()).Content = Convert.ToString(actionReturnValue, CultureInfo.InvariantCulture);
90                 (arg_29_0 as ContentResult).Content = Convert.ToString(actionReturnValue, CultureInfo.InvariantCulture);
91             }
92             return arg_29_0;
93         }
94 
95     }

At first glance, I still don't know what to do, step by step.

First, we need to know how Aggregate is implemented. Look directly at the source code as follows.

 

It's easy to understand when you look at the source code, which is to traverse the data source to loop through the delegate passed in, and take the result as a parameter to execute the delegate of the next cycle.

All I've sorted out is an easy-to-understand bunch of code.

 1     public class Class2
 2     {
 3 
 4         public void Test()
 5         {
 6             var preContext = new ActionExecutingContext();
 7 
 8             Func<ActionExecutedContext> seed = () =>
 9             {
10                 Console.WriteLine("implement action");
11                 return new ActionExecutedContext();
12             };
13 
14             int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
15 
16             Func<Func<ActionExecutedContext>, int, Func<ActionExecutedContext>> secondParam =
17             (Func<ActionExecutedContext> next, int filter) =>
18             {
19                 return () => this.getStr(next, filter, preContext);
20             };
21 
22             var reFunc2 = arr.Reverse().Aggregate<int, Func<ActionExecutedContext>>(seed, secondParam);
23             reFunc2.Invoke();
24 
25         }
26 
27         public ActionExecutedContext getStr(Func<ActionExecutedContext> func, int filter, ActionExecutingContext preContext)
28         {
29 
30             Console.WriteLine("before action----" + filter + "----" + preContext.ToString());
31 
32             var res = func.Invoke();
33 
34             Console.WriteLine("before action----" + filter + "----" + res.ToString());
35 
36             return res;
37         }
38 
39     }

I use an int array to simulate the IActionFilter set, and the rest are written in the same way as the mvc framework.

The results are as follows

 

See here, do you understand that it is through the nested delegation delegation delegation in the delegation to ingeniously implement the form of Russian dolls to achieve the implementation of the IActionFilter filter and the Action method.

Topics: Windows REST