Asp.net core mvc analysis: Action execution, coremvc

Source: Internet
Author: User

Asp.net core mvc analysis: Action execution, coremvc

Keep up with the previous article. After the route and action match, the ActionDescriptor that best matches the current request is obtained, and then the action is executed through IActionInvoker.

Let's take a look at how IActionInvoker gets it. The Code is as follows:

Context. handler = (c) => {var routeData = c. getRouteData (); // instantiate the ActionContext object var actionContext = new ActionContext (context. httpContext, routeData, actionDescriptor); if (_ actionContextAccessor! = Null) {_ actionContextAccessor. actionContext = actionContext;} // create IActionInvoker var invoker = _ actionInvokerFactory. createInvoker (actionContext); if (invoker = null) {throw new InvalidOperationException (Resources. formatActionInvokerFactory_CouldNotCreateInvoker (actionDescriptor. displayName);} // execute the invoker to process the request return invoker. invokeAsync ();};

From the code above, we can see that an IActionInvoker is created through IActionInvokerFactory. in the framework, this interface implementation class is ActionInvokerFactory. The CreateInvoker method implementation code is as follows:

public IActionInvoker CreateInvoker(ActionContext actionContext)        {            var context = new ActionInvokerProviderContext(actionContext);            foreach (var provider in _actionInvokerProviders)            {                provider.OnProvidersExecuting(context);            }            for (var i = _actionInvokerProviders.Length - 1; i >= 0; i--)            {                _actionInvokerProviders[i].OnProvidersExecuted(context);            }            return context.Result;        }

In the method, first instantiate an ActionInvokerProviderContext, and then call IActionInvokerProvider to set the context. result, context. result is an IActionInvoker, So we track the IActionInvokerProvider implementation class in the framework to see how it works. The framework provides an implementation class ControllerActionInvokerProvider and creates an IActionInvoker object in the OnProvidersExecuting method. The Code is as follows:

public void OnProvidersExecuting(ActionInvokerProviderContext context)        {            if (context == null)            {                throw new ArgumentNullException(nameof(context));            }            var actionDescriptor = context.ActionContext.ActionDescriptor as ControllerActionDescriptor;            if (actionDescriptor != null)            {                var controllerContext = new ControllerContext(context.ActionContext);                // PERF: These are rarely going to be changed, so let's go copy-on-write.                controllerContext.ValueProviderFactories = new CopyOnWriteList<IValueProviderFactory>(_valueProviderFactories);                controllerContext.ModelState.MaxAllowedErrors = _maxModelValidationErrors;                                var cacheState = _controllerActionInvokerCache.GetState(controllerContext);                context.Result = new ControllerActionInvoker(                    _controllerFactory,                    _argumentBinder,                    _logger,                    _diagnosticSource,                    controllerContext,                    cacheState.Filters,                    cacheState.ActionMethodExecutor);            }        }

From the code above, we can see that, in the end, a ControllerActionInvoker object is instantiated. The first several parameters of this class constructor are not introduced. The focus is on the last two parameters. One is IFilterMetadata [], indicates the set of filter information associated with the current action. One is ObjectMethodExecutor. From the name, we can see that this is the executor of the Controller method. The two parameters are derived from cacheState. This object is obtained by calling _ controllerActionInvokerCache. GetState (controllerContext). It is a ControllerActionInvokerState struct type and is defined as follows:

Public struct evaluate {public evaluate (IFilterMetadata [] filters, ObjectMethodExecutor actionMethodExecutor) {Filters = filters; ActionMethodExecutor = Filters;} // public IFilterMetadata [] filters {get ;} // method executor public ObjectMethodExecutor ActionMethodExecutor {get; set ;}}

The cacheState creation process is as follows:

 public ControllerActionInvokerState GetState(ControllerContext controllerContext)        {            var cache = CurrentCache;            var actionDescriptor = controllerContext.ActionDescriptor;            IFilterMetadata[] filters;            Entry cacheEntry;            if (!cache.Entries.TryGetValue(actionDescriptor, out cacheEntry))            {                var filterFactoryResult = FilterFactory.GetAllFilters(_filterProviders, controllerContext);                filters = filterFactoryResult.Filters;                var executor = ObjectMethodExecutor.Create(                    actionDescriptor.MethodInfo,                    actionDescriptor.ControllerTypeInfo);                cacheEntry = new Entry(filterFactoryResult.CacheableFilters, executor);                cacheEntry = cache.Entries.GetOrAdd(actionDescriptor, cacheEntry);            }            else            {                // Filter instances from statically defined filter descriptors + from filter providers                filters = FilterFactory.CreateUncachedFilters(_filterProviders, controllerContext, cacheEntry.FilterItems);            }            return new ControllerActionInvokerState(filters, cacheEntry.ActionMethodExecutor);        }

Let's take a look at the lines from 10th to 15th. First, get the set of filter information associated with the current action through FilterFactory, then Create an ObjectMethodExecutor object through ObjectMethodExecutor. Create, and then cache it.

In fact, here we already know that IActionInvokder is a ControllerActionInvoker, and then calls the InvokeAsync method of this object to start the action execution. The latest version uses the state machine (if it is a conceptual error, you are welcome to correct it). During execution, the status switches between different States and the processing is completed. This part of work is implemented in the Next method. The method is actually executed in the State. ActionInside State. In this State, the Controller method is executed by calling InvokeActionMethodAsync. The main code is as follows:

// The following code implements different logic var returnType = executor based on different return value types. methodReturnType; if (returnType = typeof (void) {executor. execute (controller, orderedArguments); result = new EmptyResult ();} else if (returnType = typeof (Task) {await (Task) executor. execute (controller, orderedArguments); result = new EmptyResult ();} else if (executor. taskGenericType = typeof (IActionResult) {result = await (Task <IAct IonResult>) executor. execute (controller, orderedArguments); if (result = null) {throw new InvalidOperationException (Resources. formatActionResult_ActionReturnValueCannotBeNull (typeof (IActionResult);} else if (executor. isTypeAssignableFromIActionResult) {if (_ executor. isMethodAsync) {result = (IActionResult) await _ executor. executeAsync (controller, orderedArguments);} else {result = (IActi OnResult) _ executor. Execute (controller, orderedArguments);} if (result = null) {throw new InvalidOperationException (Resources. FormatActionResult_ActionReturnValueCannotBeNull (_ executor. TaskGenericType ?? ReturnType) ;}} else if (! Executor. IsMethodAsync) {var resultAsObject = executor. Execute (controller, orderedArguments); result = resultAsObject as IActionResult ?? New ObjectResult (resultAsObject) {DeclaredType = returnType, };} else if (executor. TaskGenericType! = Null) {var resultAsObject = await executor. ExecuteAsync (controller, orderedArguments); result = resultAsObject as IActionResult ?? New ObjectResult (resultAsObject) {DeclaredType = executor. taskGenericType, };} else {// This will be the case for types which have derived from Task and Task <T> or non Task types. throw new InvalidOperationException (Resources. formatActionExecutor_UnexpectedTaskInstance (executor. methodInfo. name, executor. methodInfo. declaringType);} _ result = result ;}

Regardless of the return value type, executor. Execute is called to call the Controller method and obtain the result. executor is the ObjectMethodExecutor mentioned above. Let's just look at the implementation of the Execute method:

public object Execute(object target, object[] parameters)        {            return _executor(target, parameters);        }

It is a _ executor, which is a delegate type of delegate object ActionExecutor (object target, object [] parameters). It is created by using the GetExecutor method when instantiating ObjectMethodInvoker, the dynamic lambda expression is used. With this delegate object, you can execute the Controller method.

After the method is executed, the method returns the Result, and then enters the Result-related State process until the State. ResultInside status, the InvokeResultAsync method is called to execute the action Result. The Code is as follows:

protected async Task InvokeResultAsync(IActionResult result)        {            var actionContext = _actionContext;            _diagnosticSource.BeforeActionResult(actionContext, result);            try            {                await result.ExecuteResultAsync(actionContext);            }            finally            {                _diagnosticSource.AfterActionResult(actionContext, result);            }        }

Even if a controller action is completed, you must note that the article only introduces the main process, and many other execution statuses are not mentioned in the article.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.