.NET Core开发日志——Action
在叙述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类中探寻,主要是涉及反射相关的知识,这里就不做进一步解释了。
.NET Core开发日志——Action的更多相关文章
- .NET Core开发日志——RequestDelegate
本文主要是对.NET Core开发日志--Middleware的补遗,但是会从看起来平平无奇的RequestDelegate开始叙述,所以以其作为标题,也是合情合理. RequestDelegate是 ...
- .NET Core开发日志——Entity Framework与PostgreSQL
Entity Framework在.NET Core中被命名为Entity Framework Core.虽然一般会用于对SQL Server数据库进行数据操作,但其实它还支持其它数据库,这里就以Po ...
- C#实现多级子目录Zip压缩解压实例 NET4.6下的UTC时间转换 [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程 asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案 .NET Core开发日志
C#实现多级子目录Zip压缩解压实例 参考 https://blog.csdn.net/lki_suidongdong/article/details/20942977 重点: 实现多级子目录的压缩, ...
- .NET Core开发日志——从搭建开发环境开始
.NET Core自2016年推出1.0版本开始,到目前已是2.1版本,在其roadmap计划里明年更会推出3.0版本,发展不可不谓之迅捷.不少公司在经过一个谨慎的观望期后,也逐步开始将系统升级至最新 ...
- .NET Core开发日志——Model Binding
ASP.NET Core MVC中所提供的Model Binding功能简单但实用,其主要目的是将请求中包含的数据映射到action的方法参数中.这样就避免了开发者像在Web Forms时代那样需要从 ...
- .NET Core开发日志——简述路由
有过ASP.NET或其它现代Web框架开发经历的开发者对路由这一名字应该不陌生.如果要用一句话解释什么是路由,可以这样形容:通过对URL的解析,指定相应的处理程序. 回忆下在Web Forms应用程序 ...
- .NET Core开发日志——结构化日志
在.NET生态圈中,最早被广泛使用的日志库可能是派生自Java世界里的Apache log4net.而其后来者,莫过于NLog.Nlog与log4net相比,有一项较显著的优势,它支持结构化日志. 结 ...
- .NET Core开发日志——Edge.js
最近在项目中遇到这样的需求:要将旧有系统的一部分业务逻辑集成到新的自动化流程工具中.这套正在开发的自动化工具使用的是C#语言,而旧有系统的业务逻辑则是使用AngularJS在前端构建而成.所以最初的考 ...
- .NET Core开发日志——Linux版本的SQL Server
SQL Server 2017版本已经可以在Linux系统上安装,但我在尝试.NET Core跨平台开发的时候使用的是Mac系统,所以这里记录了在Mac上安装SQL Server的过程. 最新的SQL ...
随机推荐
- java.awt.headless 模式
1. 什么是 java.awt.headless? Headless模式是系统的一种配置模式.在系统可能缺少显示设备.键盘或鼠标这些外设的情况下可以使用该模式. 2. 何时使用和headless mo ...
- phpbbchina恢复上线
上个月已经把ICP备案重新办过了, 但是一直在忙着应付工作上的事. 从上周末开始经过数天的努力, 将 phpbbchina 恢复上线了. 时间一晃, 正好十年. 目前能找到的最新的数据是2008-10 ...
- JAVA MyBatis使用技巧收集
1. 使用事务注解. @Transactional
- 最新以及历史各版本 .NET Framework 的下载
唔,如题,详见地址:https://www.microsoft.com/net/download/windows
- 物联网架构成长之路(23)-Docker练习之Elasticsearch服务搭建
0. 前言 最近基本都是学一些环境配置,和一些中间件的安装与配置.没有实际编写代码.可能看起来有点水,我对自己的学习方式是,先要了解各个中间件的安装配置以及简单使用,理论应用场景,然后我在小项目中,逐 ...
- oracle结合mybatis批量插入数据
先上代码: controller: result = service.insertTRbXdhjLendYdData(params); service: List<TRbXdhjLendDTO& ...
- 【emWin】例程二十七:窗口对象——Listview
简介: LISTVIEW小工具可在具有多个列的列表中选择某个元素.由于LISTVIEW小工具包含了 一个HEADER小工具,因此可对列加以管理(排序等).所创建的LISTVIEW 既可以无环绕型框架窗 ...
- UML类建模(强烈推荐-思路很清晰)
UML类建模(强烈推荐-思路很清晰) 2016年10月23日 15:17:47 mbshqqb 阅读数:2315 标签: uml面向对象设计模式 更多 个人分类: 面向对象程序设计 UML的构造快 ...
- Java如何检查端口是否被使用?
在Java编程中,如何扫描打开的端口(是否被使用)? 以下示例显示如何通过创建 Socket 对象来检查主机上打开或正在使用的端口(相当于一个简单的端口扫描器). package com.yiibai ...
- 使用InstallAnywhere7.1制作Java exe程序安装包
[转[使用InstallAnywhere7.1制作Java exe程序安装包 使用InstallAnywhere7.1制作Java exe程序安装包 对于已经完成的Java应用程序开发项目,从商业化角 ...