. NET Core Development Log--controller

Source: Internet
Author: User
Tags httpcontext

After the routing workflow is clear, the next thing to consider is how the MVC framework generates the controller and the timing of its generation.

Based on previous experience with ASP. NET MVC, the controller should be built by a controllerfactory. Looking at the source code for ASP. NET Core MVC, there is a defaultcontrollerfactory class, and it's not surprising that it has a Createcontroller method.

public virtual object CreateController(ControllerContext context){    ...    var controller = _controllerActivator.Create(context);    foreach (var propertyActivator in _propertyActivators)    {        propertyActivator.Activate(context, controller);    }    return controller;}

However, the application of the Controllerfactoryprovider is only used to determine whether the incoming Controllerfactory type is defaultcontrollerfactory.

public ControllerFactoryProvider(    IControllerActivatorProvider activatorProvider,    IControllerFactory controllerFactory,    IEnumerable<IControllerPropertyActivator> propertyActivators){    ...    _activatorProvider = activatorProvider;    // Compat: Delegate to the IControllerFactory if it's not the default implementation.    if (controllerFactory.GetType() != typeof(DefaultControllerFactory))    {        _factoryCreateController = controllerFactory.CreateController;        _factoryReleaseController = controllerFactory.ReleaseController;    }    _propertyActivators = propertyActivators.ToArray();}

Then look at the Createcontrollerfactory method inside the Controllerfactoryprovider. This is more like a factory method that really creates a controller.

public Func<ControllerContext, object> CreateControllerFactory(ControllerActionDescriptor descriptor){    ...    if (_factoryCreateController != null)    {        return _factoryCreateController;    }    var controllerActivator = _activatorProvider.CreateActivator(descriptor);    var propertyActivators = GetPropertiesToActivate(descriptor);    object CreateController(ControllerContext controllerContext)    {        var controller = controllerActivator(controllerContext);        for (var i = 0; i < propertyActivators.Length; i++)        {            var propertyActivator = propertyActivators[i];            propertyActivator(controllerContext, controller);        }        return controller;    }    return CreateController;}

There are two types of creation, one using a custom factory method and the other through the Controlleractivatorprovider Createactivator method.

public Func<ControllerContext, object> CreateActivator(ControllerActionDescriptor descriptor){    ...    var controllerType = descriptor.ControllerTypeInfo?.AsType();    ...    if (_controllerActivatorCreate != null)    {        return _controllerActivatorCreate;    }    var typeActivator = ActivatorUtilities.CreateFactory(controllerType, Type.EmptyTypes);    return controllerContext => typeActivator(controllerContext.HttpContext.RequestServices, arguments: null);}

Knowing how to create a controller, here's the time to start investigating the creation of a controller.

The Createcontrollerfactory method of the Controllerfactoryprovider class is called by the Getcachedresult method of the Controlleractioninvokercache class.

Public (Controlleractioninvokercacheentry cacheentry, ifiltermetadata[] filters) Getcachedresult (controllercontext    ControllerContext) {var cache = Currentcache;    var actiondescriptor = Controllercontext.actiondescriptor;    Ifiltermetadata[] Filters; if (!cache. Entries.trygetvalue (Actiondescriptor, out var cacheentry)) {var filterfactoryresult = Filterfactory.getallfilte        RS (_filterproviders, controllercontext);        filters = filterfactoryresult.filters; var parameterdefaultvalues = parameterdefaultvalues.        Getparameterdefaultvalues (Actiondescriptor.methodinfo); var objectmethodexecutor = objectmethodexecutor.create (Actiondescriptor.methodinfo, Actiondescripto        R.controllertypeinfo, parameterdefaultvalues);        var controllerfactory = _controllerfactoryprovider.createcontrollerfactory (actiondescriptor);    var controllerreleaser = _controllerfactoryprovider.createcontrollerreleaser (actiondescriptor);    var propertybinderfactory = controllerbinderdelegateprovider.createbinderdelegate (_parameterbinder,        _modelbinderfactory, _modelmetadataprovider, actiondescriptor);        var actionmethodexecutor = Actionmethodexecutor.getexecutor (objectmethodexecutor); CacheEntry = new Controlleractioninvokercacheentry (filterfactoryresult.cacheablefilters, Controlle Rfactory, Controllerreleaser, Propertybinderfactory, Objectmethodexecutor, AC        Tionmethodexecutor); CacheEntry = cache.    Entries.getoradd (Actiondescriptor, cacheentry); } else {//filter instances from statically defined filter descriptors + from filter providers filter    s = filterfactory.createuncachedfilters (_filterproviders, ControllerContext, cacheentry.cachedfilters); } return (cacheentry, filters);}

The value is returned as part of the Controlleractioninvokercacheentry object by the method.

The upper-level caller of the Getcachedresult method is the Onprovidersexecuting method of the Controlleractioninvokerprovider class.

  public void onprovidersexecuting (Actioninvokerprovidercontext context) {... if (context. Actioncontext.actiondescriptor is Controlleractiondescriptor) {var controllercontext = new ControllerContext (CO ntext.        Actioncontext);        Perf:these is rarely going to being changed, so let's go copy-on-write. Controllercontext.valueproviderfactories = New Copyonwritelist<ivalueproviderfactory> (_        Valueproviderfactories);        ControllerContext.ModelState.MaxAllowedErrors = _maxmodelvalidationerrors;        var cacheresult = _controlleractioninvokercache.getcachedresult (ControllerContext);             var invoker = new Controlleractioninvoker (_logger, _diagnosticsource, ControllerContext,        Cacheresult.cacheentry, cacheresult.filters); Context.    Result = Invoker; }}

The Controlleractioninvokercacheentry object is also assigned a value for the result property of Actioninvokerprovidercontext as part of the Controlleractioninvoker object.

Further up the track, to the Createinvoker method of the Actioninvokerfactory class.

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;}

And its callers are Mvcroutehandler or Mvcattributeroutehandler.

public Task RouteAsync(RouteContext context){    ...    context.Handler = (c) =>    {        var routeData = c.GetRouteData();        var actionContext = new ActionContext(context.HttpContext, routeData, actionDescriptor);        if (_actionContextAccessor != null)        {            _actionContextAccessor.ActionContext = actionContext;        }        var invoker = _actionInvokerFactory.CreateInvoker(actionContext);        if (invoker == null)        {            throw new InvalidOperationException(                Resources.FormatActionInvokerFactory_CouldNotCreateInvoker(                    actionDescriptor.DisplayName));        }        return invoker.InvokeAsync();    };    ...}

The factory method where the controller was created is not actually called, and the controller does not exist at this time. Therefore, it is necessary to complete the InvokeAsync method of executing Controlleractioninvoker, or more accurately the InvokeAsync method of its base class Resourceinvoker.

public virtual async Task InvokeAsync(){    try    {        ...        using (_logger.ActionScope(_actionContext.ActionDescriptor))        {            ...            try            {                await InvokeFilterPipelineAsync();            }            ...        }    }    ...}

Starting with the Invokefilterpipelineasync approach, a series of processes will be phased out according to different states.

private async Task InvokeFilterPipelineAsync(){    var next = State.InvokeBegin;    var scope = Scope.Invoker;    var state = (object)null;    var isCompleted = false;    while (!isCompleted)    {        await Next(ref next, ref scope, ref state, ref isCompleted);    }}

To State.actionbegin this step (the next method of the Controlleractioninvoker Class), we can finally find out where the controller factory method is executed.

private Task Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted){    switch (next)    {        case State.ActionBegin:            {                var controllerContext = _controllerContext;                _cursor.Reset();                _instance = _cacheEntry.ControllerFactory(controllerContext);                _arguments = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);                var task = BindArgumentsAsync();                if (task.Status != TaskStatus.RanToCompletion)                {                    next = State.ActionNext;                    return task;                }                goto case State.ActionNext;            }        ...        }                }}            

Finally, a flowchart summarizes the search process.

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.