当前位置: 移动技术网 > IT编程>开发语言>.net > .NET Core开发日志——Action

.NET Core开发日志——Action

2018年08月19日  | 移动技术网IT编程  | 我要评论

在叙述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类中探寻,主要是涉及反射相关的知识,这里就不做进一步解释了。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网