通过表达式树构建URL时正确识别ActionNameAttribute
在MvcFutures项目中提供了一个辅助方法,可以将一个表达式树对象转化成一个RouteValueDictionary集合。只可惜,这个辅助方法的毛病比较多。例如,它直接把方法名作为action的值,而忽略了其上标记的ActionNameAttribute。这导致了某个被“改名”的Action方法一旦用在了表达式树中,最终得到的URL便是错误的。例如有一个Action方法:
public class HomeController : Controller {[ActionNamepublic ViewResult Index(){...} }
如果您使用这样的方式来生成URL(ActionEx方法的实现请参考《使用表达式树构建DomainRoute的URL》):
<a href="<%= Url.ActionEx<HomeController>(c => c.Index()) %>">Home</a>
则最终得到的代码是:
<a href="/Home/Index">Home</a>
而我们需要的结果应该是:
<a href="/Home/Default">Home</a>
正是因为这个原因(以及一些其他因素),许多朋友放弃使用强类型的方式构造URL。不过,如果您继续看下去,就会发现这个功能其实非常简单。只要做稍微一点点修改就可以了。不过现在,让我们来观察MvcFutures是如何实现这部分功能的。我已经把相关的代码复制到自己的RouteExpression类中:
public static class RouteExpression {public static RouteValueDictionary GetRouteValues<TController>(Expression<Action<TController>> action)where TController : Controller{if (action == null){throw new ArgumentNullException("action");}MethodCallExpression call = action.Body as MethodCallExpression;if (call == null){throw new ArgumentException("The action must be a method call.", "action");}string controllerName = typeof(TController).Name;if (!controllerName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)){throw new ArgumentException("The controller name must end with 'Controller'.", "action");}controllerName = controllerName.Substring(0, controllerName.Length - "Controller".Length);if (controllerName.Length == 0){throw new ArgumentException("Cannot route to the Controller class", "action");}var rvd = new RouteValueDictionary();rvd.Add("controller", controllerName);rvd.Add("action", call.Method.Name);AddParameterValuesFromExpressionToDictionary(rvd, call);return rvd;}private static void AddParameterValues(RouteValueDictionary rvd, MethodCallExpression call){...} }
这段代码大部分内容都是进行参数校验,一旦出现以下情况之一,便会抛出异常:
- 表达式树为null。
- 表达式树不是一个MethodCallExpression(应该是一个Action方法的调用)
- 如果控制器类型的名称不以Controller结尾(破坏了约定)
- 如果控制器类型的名称就是Controller
经过校验之后,这个方法根据控制器类型的名称计算出controller(HomeController => Home),再把所调方法的名称作为action(Index() => Index)。最后,再使用AddParameterValues方法获得参数,并填充RouteValueDictionary(关于这点我们下次再来讨论)。
不过,问题就出现在从Action方法的MethodInfo“直接获取”名称这个步骤上。这个MethodInfo可能还标记着ActionNameAttribute呢,它的Name属性可不是action的名称。为此,我们必须多做这么一步:
private static ReaderWriterLockSlim s_rwLock = new ReaderWriterLockSlim(); private static Dictionary<MethodInfo, string> s_actionNames = new Dictionary<MethodInfo, string>();private static string GetActionName(MethodInfo methodInfo) {string actionName = null;s_rwLock.EnterReadLock();try{if (s_actionNames.TryGetValue(methodInfo, out actionName)){return actionName;}}finally{s_rwLock.ExitReadLock();}var attribute = (ActionNameAttribute)methodInfo.GetCustomAttributes(typeof(ActionNameAttribute), false).SingleOrDefault();actionName = attribute == null ? methodInfo.Name : attribute.Name;s_rwLock.EnterWriteLock();try{s_actionNames[methodInfo] = actionName;}finally{s_rwLock.ExitWriteLock();}return actionName; }
在GetActionName方法的中部则是获得action名称的代码。它会根据methodInfo上的ActionNameAttribute标记情况来确定。如果标记了ActionNameAttribute,则使用Attribute的Name属性作为action名称,否则就使用MethodInfo对象的Name属性。获得action名称之后,我们会将其保存在一个字典中。至于使用ReaderWriterLockSlim来控制并发读写的方式已经成为了标准,您甚至可以将其封装为一个组件避免重复编写相同的代码。
最后,我们把原来GetRouteValues方法中的一行代码加以替换即可:
public static RouteValueDictionary GetRouteValues<TController>(Expression<Action<TController>> action)where TController : Controller
{...rvd.Add("action", call.Method.Name);rvd.Add("action", GetActionName(call.Method));...
}
ASP.NET MVC给了我们充分的自由度定制需要的组件。从中我们也可以了解到如何在项目中编写合适的API。其实很多东西只要多走一步就会美好很多,例如这个例子,需要花费您超过半小时的时间吗?
通过表达式树构建URL时正确识别ActionNameAttribute相关推荐
- C++ 学习笔记之(19) new、delete表达式、RTTI(运行时类型识别)、枚举、类成员指针、嵌套类、局部类、位域、volatile、extern C
C++ 学习笔记之(19) new.delete表达式.RTTI(运行时类型识别).枚举.类成员指针.嵌套类.局部类.位域.volatile.extern C C++ 学习笔记之(19) new.de ...
- ASP.NET Core中使用表达式树创建URL
当我们在ASP.NET Core中生成一个action的url会这样写: var url=_urlHelper.Action("Index", "Home"); ...
- 表达式树练习实践:入门基础
什么是表达式树 来自微软官方文档的定义: 表达式树以树形数据结构表示代码. 它能干什么呢? 你可以对表达式树中的代码进行编辑和运算.这样能够动态修改可执行代码.在不同数据库中执行 LINQ 查询以及创 ...
- 关于Expression表达式树的拼接
关于Expression表达式树的拼接 最近在做项目中遇到一个问题,需求是这样的: 我要对已经存在的用户进行检索,可以根据用户的id 或者用户名其中的一部分字符来检索出来,这样就出现了三种情况 只有i ...
- 表达式树 php,Linux_LINQ学习笔记:表达式树,构建查询表达式
本节中, 我们 - phpStudy...
构建查询表达式 本节中, 我们假设我们拥有一个这样的实体类: 1: [Table] public partial class Product 2: 3: { 4: 5: [Column(IsPrima ...
- 波兰表达式 构建 表达式树
这里提供一种将波兰表达式构建成表达式树的一种方法. 二叉树的节点有三个成员:数值(或者操作符)type,左节点lnode(被操作数),右节点rnode(操作数) //借助栈将波兰表达式 构建 表达式树 ...
- 解决AD22拼版导出Gerber时DFM无法正确识别板框
网上大多方法到处Gerber时单板是可以正确识别板框的 但是 拼版时dfm软件会识别多个板框导致板厂没法做板子 于是为了少花点钱 想了个小法子来让CAM软件正确识别我的板框 就像这样子 要么是有多个o ...
- 为给定的Lambda表达式构建表达式树
这是用于将给定的LINQ表达式转换为对应的表达式树的代码示例. //using LINQAlias = System.Linq.Expressions; List<Host> dinner ...
- C#复习笔记(4)--C#3:革新写代码的方式(Lambda表达式和表达式树)
Lambda表达式和表达式树 先放一张委托转换的进化图 看一看到lambda简化了委托的使用. lambda可以隐式的转换成委托或者表达式树.转换成委托的话如下面的代码: Func<string ...
- ai端到端_如何使用行为树构建端到端的对话式AI系统
ai端到端 by Lior Messinger 由Lior Messinger 如何使用行为树构建端到端的对话式AI系统 (How to Build an End-to-End Conversatio ...
最新文章
- raid0、raid1、raid5、raid10 flash
- LINGO 12安装教程
- leader选举的源码分析-FastLeaderElection.starter
- 数学--数论--莫比乌斯函数
- 华为2018软件岗笔试题解题思路和源代码分享
- C++ 类访问控制public/private/protected探讨
- vba 根据分辨率 调整窗口显示比例_2020 如何选择适合自己的显示器?小白选购电脑显示器必看,附各类型显示器高性价比选购指南分析...
- HTML_CSS_JS_JSON
- Spring-@Value
- Recoil 是 React 的状态管理库
- frontend-tools
- Visio 2016专业版 激活方式
- 自然语言处理-错字识别(基于Python)kenlm、pycorrector
- MPC控制笔记(一)
- Java8新特性详解
- 游戏引擎架构 (Jason Gregory 著)
- 所谓语音合成 是计算机根据语言学,计算机语音合成在GAI中的应用
- 谷歌人工智能设计的芯片揭示什么是智能的本质
- service中间调用dao层方法时,出现Mapper method ‘dao.xxx‘ has an unsupported return type错误,但是sql执行修改数据成功,并没有回滚
- 华为畅享9额android9,华为畅享9
热门文章
- c语言 com组件,com组件 C语言基础.ppt
- python用时间戳给文件命名规则_关于时间戳:python复制文件但保持原始
- nginx 超时设置_Nginx最详细的反向代理配置步骤,拿去不谢
- SpringBoot在前端发送url时,不能识别特殊字符的问题
- mysql锁问题吗_Mysql锁的问题和解析
- USACO翻译:USACO 2013 DEC Silver三题
- HDU - 6297 CCPC直播
- SpringMVC拦截器(包括自定以拦截器--实现HandlerInterceptorAdapter)(资源和权限管理)...
- Git学习系列之Git基本操作推送项目(图文详解)
- 使用ELK在DC / OS中进行日志管理