Series directory
In ASP. NET MVC2IactionfilterAndIresultfilterYou can see the following four methods in the execution logic (you can find them in controlleractioninvoker. CS)
- Invokeactionmethodwithfilters
- Invokeactionmethodfilter
- Invokeactionresultwithfilters
- Invokeactionresultfilter
In fact, the implementation and logic of the first two and the last two are almost the same, but only one group of ProcessingIactionfilter, A group of processesIresultfilterHere I will only discuss the first two groups.
Before reading this article, we recommend that you understand the. NET func <> delegate, set aggregate method, anonymous method, and other related knowledge.
Let's take a lookInvokeactionresultwithfiltersIt means to execute the action that contains the iactionfilter filter:
Protected virtual actionexecutedcontext parameters (controllercontext, ilist <iactionfilter> filters, actiondescriptor, idictionary <string, Object> parameters) {controllercontext = new parameters (controllercontext, parameters, parameters ); func <actionexecutedcontext> continuation = () => New actionexecutedcontext (controllercontext, actiondescriptor, false/* canceled */, null/* exception */) {result = invokeactionmethod (controllercontext, actiondescriptor, parameters)}; // need to reverse the Filter list because the continuations are built up backward func <actionexecutedcontext> thunk = filters. reverse (). aggregate (continuation, (next, filter) => () => invokeactionmethodfilter (filter, precontext, next); Return thunk ();}
I don't plan to go back to the call point of the function, so here I need to explain the background of the function being called and the meaning of each input parameter. Before calling a function, all the preparations have been completed. These preparations include:Locate action. All iactionfilters and iresultfilters related to this action have been reflected and saved. They have passed the iauthorizationfilter verification and the Action parameter has been properly set.
- Controllercontext: Needless to say, the context parameters encapsulated during the execution of the controller and action are not covered in this article;
- Ilist <iactionfilter> filters: Set of iactionfilter interfaces related to this action,This article will discuss. Note that the set contains the iactionfilter implemented by the Controller of the action. Therefore, the set contains at least one iactionfilter;
- Actiondescriptor: encapsulate the description of the action and execute it to actually execute the action. This article does not cover it;
- Idictionary <string, Object> parameters: Provides the parameters required by action. This document does not cover.
Interpreting invokeactionresultwithfilters line by line
Actionexecutingcontext precontext = new actionexecutingcontext (controllercontext, actiondescriptor, parameters );
We know thatAciton has more than one iactionfilterThey are executed in some order.PrecontextObject equivalentAccept in sequenceThe iactionfilterOnactionexecuting result.
Func <actionexecutedcontext> continuation = () => New actionexecutedcontext (controllercontext, actiondescriptor, false/* canceled */, null/* exception */) {result = invokeactionmethod (controllercontext, actiondescriptor, parameters )};
Func <actionexecutedcontext> continuation is a delegate that points to the function that returns actionexecutedcontext. The second half is an anonymous method.
() => New actionexecutedcontext (controllercontext, actiondescriptor, false/* canceled */, null/* exception */) {result = invokeactionmethod (controllercontext, actiondescriptor, parameters )};
Indicates that no parameter is input. An actionexecutedcontext is returned and,The result indicates the result after the actual action is executed.. This anonymous method is executed when continuation () is executed.
So far, continuation is a delegate object, and its function is to actually execute action.
Func <actionexecutedcontext> thunk = filters. Reverse (). Aggregate (continuation, (next, filter) => () => invokeactionmethodfilter (filter, precontext, next ));
It is also a func <actionexecutedcontext> delegate thunk, which is defined asResult of the aggregate Method(Remember, Here aggregate returns a delegate object.) before aggregation, you must first reverse the ilist <iactionfilter> filters. The second parameter of aggregate indicates:Take each filter in filters and the result of the previous aggregation operation as a parameter and return a delegate object of func <actionexecutedcontext>., Which is:
() => Invokeactionmethodfilter (filter, precontext, next)
The delegate object itself executes invokeactionmethodfilter. This method requires the current filter and the result of the previous aggregation operation (a func <actionexecutedcontext> delegate object) as the parameter.The initial func <actionexecutedcontext> delegate object of the entire aggregation operation is the continuation instantiated above..
I can't find any other rhetoric to describe this section.CodeFor more information, see
The orange icon on the left indicates the iactionfilter In the filters set, and the red arrow indicates the aggregation operation. The result of each aggregation operation is a func <actionexecutedcontext> delegate object, and the final aggregation result is assigned to thunk. This aggregate object calls invoke a function called invokeationmethodfilter, and one of the parameters points to the result of the previous aggregate object (that is, the next in the Code ).
Note: The aggregation result returns only one delegate, and the method has not been executed.The actual execution is as follows:
Return thunk ();
This will result in the execution of the function to which the delegate object is finally returned.
Now it's time to seeInvokeationmethodfilterHow is the function executed? I will only paste the upper part first:
internal static actionexecutedcontext invokeactionmethodfilter (iactionfilter filter, actionexecutingcontext precontext, func
continuation) {filter. onactionexecuting (precontext); If (precontext. R Esult! = NULL) {return New actionexecutedcontext (precontext, precontext. actiondescriptor, true/* canceled */, null/* exception */) {result = precontext. result };} bool waserror = false; actionexecutedcontext postcontext = NULL; try {postcontext = continuation ();}...
First, call the onactionexecuting of the filter, and then determine whether the precontext. result is modified. If it is null, the filter is passed!Then execute continuation in try.. Do you still remember what continuation is?Is a reference to the previous func <actionexecutedcontext> delegate object,Calling here means recursively calling the previous invokeactionmethodfilter! If this recursive chain can pass the layer-by-layer check of each filter during execution, the call will finally be the real execution action!
Now we know that the aggregate method actually constructs a recursive call chain. Now we know why the result of the previous aggregate object is named next and continuation: this is relative to the execution time, of course, "Next" or "continue". I have to admire Microsoft engineers for cleverly constructing a recursive chain with several lines of code. Let's take a look at the Catch Block Code in the lower half:
catch (threadabortexception) {// This type of exception occurs as a result of response. redirect (), but we special-case so that // The filters don't see this as an error. postcontext = new actionexecutedcontext (Prec Ontext, precontext. actiondescriptor, false/* canceled */, null/* exception */); filter. onactionexecuted (postcontext); throw;} catch (exception ex) {waserror = true; postcontext = new actionexecutedcontext (precontext, precontext. actiondescriptor, false/* canceled */, ex); filter. onactionexecuted (postcontext); If (! Postcontext. exceptionhandled) {Throw ;}} if (! Waserror) {filter. onactionexecuted (postcontext) ;}return postcontext ;}
The catch contains two parts. The comment in the first catch already illustrates the problem. Think about the second Catch Block and you will understand ASP in depth. net MVC (8): the reason why the current action exception is captured at the previous level, because a recursive call is included in try \ catch.
Finally, let's talk about the reverse execution before aggregate. If you try to debug this code, or you have studied the previous Code details, I will tell you that when you first enter invokeactionmethodwithfilters, the first object of the filters parameter is the Controller's own filter, you will not be surprised. Imagine if there is no reversal here, the result of direct aggregation will be the final execution of the Controller's own filter, and the design of MVC is to make the Controller's own filter take precedence over other attributes.
It took me a lot of time to understand this piece of code. when I figured it out, it was really suddenly clear and I was so surprised! With a few clicks, an elegant and clean recursive call is formed, and traces of recursion cannot be found without looking at it carefully!
Labor fruit, reproduced please indicate the source: http://www.cnblogs.com/P_Chou/archive/2010/12/18/asp-net-mvc-src-recursive-using-aggregate-delegate.html