欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

.NET Core开发日志——Action

程序员文章站 2024-01-04 20:20:16
在叙述 "Controller" 一文中,有一处未做解释,即CreateControllerFactory方法中ControllerActionDescriptor参数是如何产生的。这是因为其与Action的关联性更大,所以放在本文中继续描述。 回到MvcRouteHandler或者MvcAttri ......

在叙述controller一文中,有一处未做解释,即createcontrollerfactory方法中controlleractiondescriptor参数是如何产生的。这是因为其与action的关联性更大,所以放在本文中继续描述。

回到mvcroutehandler或者mvcattributeroutehandler的方法中:

public task routeasync(routecontext context)
{
    ...

    var candidates = _actionselector.selectcandidates(context);
    if (candidates == null || candidates.count == 0)
    {
        _logger.noactionsmatched(context.routedata.values);
        return task.completedtask;
    }

    var actiondescriptor = _actionselector.selectbestcandidate(context, candidates);
    if (actiondescriptor == null)
    {
        _logger.noactionsmatched(context.routedata.values);
        return task.completedtask;
    }

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

    ...
}

不难发现作为源头的actioncontext中传入了actiondescriptor,而这个参数的值是在actionselector中被筛选出来的。

public ireadonlylist<actiondescriptor> selectcandidates(routecontext context)
{
    ...

    var cache = current;

    // the cache works based on a string[] of the route values in a pre-calculated order. this code extracts
    // those values in the correct order.
    var keys = cache.routekeys;
    var values = new string[keys.length];
    for (var i = 0; i < keys.length; i++)
    {
        context.routedata.values.trygetvalue(keys[i], out object value);

        if (value != null)
        {
            values[i] = value as string ?? convert.tostring(value);
        }
    }
    
    if (cache.ordinalentries.trygetvalue(values, out var matchingroutevalues) ||
        cache.ordinalignorecaseentries.trygetvalue(values, out matchingroutevalues))
    {
        debug.assert(matchingroutevalues != null);
        return matchingroutevalues;
    }

    _logger.noactionsmatched(context.routedata.values);
    return emptyactions;
}

然后可供筛选的actiondescriptors集合又是来自actiondescriptorcollectionprovider类。

private cache current
{
    get
    {
        var actions = _actiondescriptorcollectionprovider.actiondescriptors;
        var cache = volatile.read(ref _cache);

        if (cache != null && cache.version == actions.version)
        {
            return cache;
        }

        cache = new cache(actions);
        volatile.write(ref _cache, cache);
        return cache;
    }
}

它的内部又再调用了controlleractiondescriptorprovider类的onprovidersexecuting方法。

public actiondescriptorcollection actiondescriptors
{
    get
    {
        if (_collection == null)
        {
            updatecollection();
        }

        return _collection;
    }
}

private void updatecollection()
{
    var context = new actiondescriptorprovidercontext();

    for (var i = 0; i < _actiondescriptorproviders.length; i++)
    {
        _actiondescriptorproviders[i].onprovidersexecuting(context);
    }

    for (var i = _actiondescriptorproviders.length - 1; i >= 0; i--)
    {
        _actiondescriptorproviders[i].onprovidersexecuted(context);
    }

    _collection = new actiondescriptorcollection(
        new readonlycollection<actiondescriptor>(context.results),
        interlocked.increment(ref _version));
}

调用链继续深入到defaultapplicationmodelprovider之中。

public void onprovidersexecuting(actiondescriptorprovidercontext context)
{
    if (context == null)
    {
        throw new argumentnullexception(nameof(context));
    }

    foreach (var descriptor in getdescriptors())
    {
        context.results.add(descriptor);
    }
}

protected internal ienumerable<controlleractiondescriptor> getdescriptors()
{
    var applicationmodel = buildmodel();
    applicationmodelconventions.applyconventions(applicationmodel, _conventions);
    return controlleractiondescriptorbuilder.build(applicationmodel);
}

protected internal applicationmodel buildmodel()
{
    var controllertypes = getcontrollertypes();
    var context = new applicationmodelprovidercontext(controllertypes);

    for (var i = 0; i < _applicationmodelproviders.length; i++)
    {
        _applicationmodelproviders[i].onprovidersexecuting(context);
    }

    for (var i = _applicationmodelproviders.length - 1; i >= 0; i--)
    {
        _applicationmodelproviders[i].onprovidersexecuted(context);
    }

    return context.result;
}

private ienumerable<typeinfo> getcontrollertypes()
{
    var feature = new controllerfeature();
    _partmanager.populatefeature(feature);

    return feature.controllers;
}

到了这里终于可以看到action的影子,虽然现在还只是actionmodel。

public virtual void onprovidersexecuting(applicationmodelprovidercontext context)
{
    ...

    foreach (var controllertype in context.controllertypes)
    {
        var controllermodel = createcontrollermodel(controllertype);
        if (controllermodel == null)
        {
            continue;
        }

        context.result.controllers.add(controllermodel);
        controllermodel.application = context.result;

        ...

        foreach (var methodinfo in controllertype.astype().getmethods())
        {
            var actionmodel = createactionmodel(controllertype, methodinfo);
            if (actionmodel == null)
            {
                continue;
            }

            actionmodel.controller = controllermodel;
            controllermodel.actions.add(actionmodel);

            foreach (var parameterinfo in actionmodel.actionmethod.getparameters())
            {
                var parametermodel = createparametermodel(parameterinfo);
                if (parametermodel != null)
                {
                    parametermodel.action = actionmodel;
                    actionmodel.parameters.add(parametermodel);
                }
            }
        }
    }
}

利用controlleractiondescriptorbuilder类的build方法,可以得到预期的controlleractiondescriptor。

public static ilist<controlleractiondescriptor> build(applicationmodel application)
{
    var actions = new list<controlleractiondescriptor>();

    var methodinfomap = new methodtoactionmap();

    var routetemplateerrors = new list<string>();
    var attributeroutingconfigurationerrors = new dictionary<methodinfo, string>();

    foreach (var controller in application.controllers)
    {
        // only add properties which are explicitly marked to bind.
        // the attribute check is required for modelbinder attribute.
        var controllerpropertydescriptors = controller.controllerproperties
            .where(p => p.bindinginfo != null)
            .select(createparameterdescriptor)
            .tolist();
        foreach (var action in controller.actions)
        {
            // controllers with multiple [route] attributes (or user defined implementation of
            // iroutetemplateprovider) will generate one action descriptor per iroutetemplateprovider
            // instance.
            // actions with multiple [http*] attributes or other (iroutetemplateprovider implementations
            // have already been identified as different actions during action discovery.
            var actiondescriptors = createactiondescriptors(application, controller, action);

            foreach (var actiondescriptor in actiondescriptors)
            {
                actiondescriptor.controllername = controller.controllername;
                actiondescriptor.controllertypeinfo = controller.controllertype;

                addapiexplorerinfo(actiondescriptor, application, controller, action);
                addroutevalues(actiondescriptor, controller, action);
                addproperties(actiondescriptor, action, controller, application);

                actiondescriptor.boundproperties = controllerpropertydescriptors;

                if (isattributeroutedaction(actiondescriptor))
                {
                    // replaces tokens like [controller]/[action] in the route template with the actual values
                    // for this action.
                    replaceattributeroutetokens(actiondescriptor, routetemplateerrors);
                }
            }

            methodinfomap.addtomethodinfo(action, actiondescriptors);
            actions.addrange(actiondescriptors);
        }
    }

    ...

    return actions;
}

controlleractiondescriptor包含了足以构建controller与action的属性。

public string controllername { get; set; }

public virtual string actionname { get; set; }

public methodinfo methodinfo { get; set; }

public typeinfo controllertypeinfo { get; set; }

public ilist<parameterdescriptor> parameters { get; set; }

controller的构建已经介绍过了,现在该谈谈关于action的。

先找到创建controlleractioninvokercacheentry对象的controlleractioninvokercache类的getcachedresult方法。可以看到两个关键参数objectmethodexecutor与actionmethodexecutor的创建方式。

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 objectmethodexecutor = objectmethodexecutor.create(
            actiondescriptor.methodinfo,
            actiondescriptor.controllertypeinfo,
            parameterdefaultvalues);

        ...

        var actionmethodexecutor = actionmethodexecutor.getexecutor(objectmethodexecutor);

        cacheentry = new controlleractioninvokercacheentry(
            filterfactoryresult.cacheablefilters, 
            controllerfactory, 
            controllerreleaser,
            propertybinderfactory,
            objectmethodexecutor,
            actionmethodexecutor);
        cacheentry = cache.entries.getoradd(actiondescriptor, cacheentry);
    }
    ...

    return (cacheentry, filters);
}

再到controlleractioninvoker类的next方法中跟踪到state.actioninside环节:

case state.actioninside:
    {
        var task = invokeactionmethodasync();
        if (task.status != taskstatus.rantocompletion)
        {
            next = state.actionend;
            return task;
        }

        goto case state.actionend;
    }

终于可以找到创建action的方法。

private async task invokeactionmethodasync()
{
    var controllercontext = _controllercontext;
    var objectmethodexecutor = _cacheentry.objectmethodexecutor;
    var controller = _instance;
    var arguments = _arguments;
    var actionmethodexecutor = _cacheentry.actionmethodexecutor;
    var orderedarguments = preparearguments(arguments, objectmethodexecutor);

    var diagnosticsource = _diagnosticsource;
    var logger = _logger;

    iactionresult result = null;
    try
    {
        diagnosticsource.beforeactionmethod(
            controllercontext,
            arguments,
            controller);
        logger.actionmethodexecuting(controllercontext, orderedarguments);
        var stopwatch = valuestopwatch.startnew();
        var actionresultvaluetask = actionmethodexecutor.execute(objectmethodexecutor, controller, orderedarguments);
        if (actionresultvaluetask.iscompletedsuccessfully)
        {
            result = actionresultvaluetask.result;
        }
        else
        {
            result = await actionresultvaluetask;
        }

        _result = result;
        logger.actionmethodexecuted(controllercontext, result, stopwatch.getelapsedtime());
    }
    ...
}

核心的代码是这一句actionmethodexecutor.execute(objectmethodexecutor, controller, orderedarguments)

actionmethodexecutor与objectmethodexecutor即是之前生成controlleractioninvokercacheentry对象时传入的两个参数,controller是在state.actionbegin环节通过_instance = _cacheentry.controllerfactory(controllercontext);生成的。orderedarguments是action方法所需的参数。

至于更详细的创建过程,可以到actionmethodexecutor类与objectmethodexecutor类中探寻,主要是涉及反射相关的知识,这里就不做进一步解释了。

上一篇:

下一篇: