表达式树是LINQ To everything 的基础,同时各种类库的Fluent API也 大量使用了Expression Tree。还记得我在不懂expression tree时,各种眼花缭乱的API 看的我各种膜拜,当我熟悉expression tree 后恍然大悟,不用看代码也能知道别人的API 是如何设计的(^_^)。 接下来这篇博客就谈谈如何使用expression tree扩展MVC中的HtmlHelper和UrlHelper。

场景

当我们在MVC中生成一个action的url会这样写:var url=UrlHelper.Action("index", "Home"); 如果要render一个action时会这样写:Html.RenderAction("index", "Home");

这样的写法瑕疵在于我们传递了两个字符串类型的参数在代码中,而我们又免不了对action和controller做重命名操作:index->default, 即便是你用resharper这样的工具重命名也无法将UrlHelper.Action("index", "Home"); 改变为UrlHelper.Action("default", "Home");

vs甚至在编译时都不会检查出来这个错误。 所以我们的目标是:设计出具有静态检查的API,让vs 提示出这个错误来,甚至是重命名时直接把相关代码都能重命名。

使用Expression Tree 重新设计这两组API

目标:设计出类似的API:Url.Action((HomeController c) => c.Index());

1.很明显我们需要在UrlHelper上写个扩展方法:

    public static string Action<TController>(
this UrlHelper url,
Expression<Func<TController, ActionResult>> actionSelector,
string protocol = null,
string hostname = null)
{
var action = "Index"; //待解析
var controller = "Home";//带解析
var routeValues = new RouteValueDictionary();//待解析 return url.Action(action, controller, routeValues, protocol, hostname);
}

现在只需要根据表达式Expression<Func<TController, ActionResult>> actionSelector 解析出action,controller,还有routeValues即可

2.解析出controller 的名称

分析:controller的名称可以根据泛型方法中的泛型参数TController得到

         private static string GetControllerName(Type controllerType)
{
var controllerName = controllerType.Name.EndsWith("Controller")
? controllerType.Name.Substring(0, controllerType.Name.Length - "Controller".Length)
: controllerType.Name;
return controllerName;
}

3.解析action的名称

分析:由于表达式Expression<Func<TController, ActionResult>> actionSelector 是一个MethodCallExpression, 所以可以很容易得到action的名称:var action=call.Method.Name;

这样已经完成了最简单的url构造方案, 但是我们还没有处理带有参数的action类型,例如:Url.Action((HomeController c) => c.Detail(10, 20));

4.解析routeValues

分析:action中的参数实际上就是MethodCallExpression中的参数,我们解析这个expression的参数即可,然后得到RouteValues

        private static RouteValueDictionary GetRouteValues(MethodCallExpression call)
{
var routeValues = new RouteValueDictionary(); var args = call.Arguments;
ParameterInfo[] parameters = call.Method.GetParameters();
var pairs = args.Select((a, i) => new
{
Argument = a,
ParamName = parameters[i].Name
});
foreach (var argumentParameterPair in pairs)
{
string name = argumentParameterPair.ParamName;
object value = argumentParameterPair.Argument.GetValue();
if (value != null)
{
var valueType = value.GetType();
if (valueType.IsValueType)
{
routeValues.Add(name, value);
}
throw new NotSupportedException("unsoupported parameter type {0}".FormatWith(value.ToString()));
}
}
return routeValues;
}

如此一来,类似Url.Action((HomeController c) => c.Detail(10, 20));这样的action也可以构造出Url了。

     if (valueType.IsValueType)
{
routeValues.Add(name, value);
}

此代码并不支持复杂类型的参数,对于action中传入复杂的类型,比如:

    public class User
{
public int Age { get; set; }
public string Email { get; set; }
}

如果Action中的参数使用了User类型:

        public ActionResult SayHelloToUser(User user)
{
return new EmptyResult();
}

如何解析呢?

   var properties = PropertyInfoHelper.GetProperties(valueType);
foreach (var propertyInfo in properties)
{
routeValues.Add(propertyInfo.Name, propertyInfo.GetValue(value));
}

大功告成,现在已经完美解决了各种类型的参数传入。

同样的道理,我们可以扩展HtmlHelper 的 RenderAction(), ActionLink()….

缺陷

早在09年,jeffery zhao就发表了lambda方式生成url的博客,对比了几种方案的性能问题,并且给出了优化方案,当然,我在写这篇博客的时候还没有真正尝试去优化这个方案,只是再次拜读了大神的方案,记得早些年就读过这些文章,但是今天重新读过仍然获益匪浅,不由得感叹几句,莫非跑题了;-);-)

接下来我会思考这个优化的问题。

结束语:本文使用Expression tree 扩展了HtmlHelper和UrlHelper,给出了一个具有静态检查的API实现方式。本文章所使用的源码提供下载,转载请注明出处

Expression Tree 扩展MVC中的 HtmlHelper 和 UrlHelper的更多相关文章

  1. MVC中的HtmlHelper详解

    熟悉MVC开发的朋友都应该知道在MVC中,每一个Controller都对应一个View,并且CS文件和对应的ASPX文件也被分离了,更重要的是不再有服务器端控件在工具箱中,不再是代码后至了.MVC中的 ...

  2. MVC中的HtmlHelper

    authour: chenboyi updatetime: 2015-04-27 21:57:17 friendly link:   目录: 1,思维导图 2,CodeSimple 1.思维导图:

  3. MVC中使用HTML Helper类扩展HTML控件

    文章摘自:http://www.cnblogs.com/zhangziqiu/archive/2009/03/18/1415005.html MVC在view页面,经常需要用到很多封装好的HTML控件 ...

  4. 【转】MVC中的扩展点

    原文地址:http://www.cnblogs.com/xfrog/tag/MVC/      MVC中的扩展点(十)辅助方法   MVC中的扩展点(九)验证   MVC中的扩展点(八)模型绑定   ...

  5. Enum扩展及MVC中DropDownListFor扩展方法的使用

    public enum SearchState { /// <summary> /// 全部 /// </summary> [Description("全部" ...

  6. MVC中HtmlHelper用法大全参考

    MVC中HtmlHelper用法大全参考 解析MVC中HtmlHelper控件7个大类中各个控件的主要使用方法(1) 2012-02-27 16:25 HtmlHelper类在命令System.Web ...

  7. 本版本延续MVC中的统一验证机制~续的这篇文章,本篇主要是对验证基类的扩展和改善(转)

    本版本延续MVC中的统一验证机制~续的这篇文章,本篇主要是对验证基类的扩展和改善 namespace Web.Mvc.Extensions { #region 验证基类 /// <summary ...

  8. 关于CI中的MVC以及扩展CI中的控制器

    MVC是一种设计模式模式,M(模型)—V(视图)—C(控制器): MVC的核心思想是强制开发者在进行项目开发时,将数据的输入,处理,输出分开编写: 1.入口文件:该文件是唯一一个给浏览器直接请求的脚本 ...

  9. MVC中的扩展点(六)ActionResult

    ActionResult是控制器方法执行后返回的结果类型,控制器方法可以返回一个直接或间接从ActionResult抽象类继承的类型,如果返回的是非ActionResult类型,控制器将会将结果转换为 ...

随机推荐

  1. modesim测试语句

    : 'd2; Reg2 <= Reg1; i <= i + 1'b1; join : 'd2; i <= i + 1'b1; join : 'd2; Reg2 <= Reg1; ...

  2. [linux]windows无法访问samba的安全性问题(关闭selinux)

    背景 在某一天重启了虚拟机的 linux 之后,我的 windows 在连接上 samba 之后,点击某些文件夹的时候,会出现没有权限打开的情况.这问题折腾了我一度重新配置了好几次 samba 的配置 ...

  3. ServiceStack.OrmLite中的一些"陷阱"(1)

    使用过ServiceStack.Ormlite的人都应该知道,其作为一个轻量级的ORM,使用的便捷度非常高,用起来就一个字:爽!而支撑其便捷度的,是库内大量地使用了扩展方法及静态变量. 首先先从源头入 ...

  4. SQL Server 大数据量分页建议方案

    简单的说就是这个 select top(20) * from( select *, rowid = row_number() over(order by xxx) from tb with(noloc ...

  5. jQuery外链新窗口打开

    对于外链,为了留住用户在本站,我们通常会使用新窗口打开,你可以设置target="_blank".然而手动一个是麻烦,另一个则是有可能会遗漏,本文通过jQuery查询要点击的链接, ...

  6. JAVA SSH 框架介绍

    SSH 为 struts+spring+hibernate 的一个集成框架,是目前较流行的一种JAVA Web应用程序开源框架. Struts Struts是一个基于Sun J2EE平台的MVC框架, ...

  7. Git常用

    创建本地库 mkdir [dirname] cd [dirname] git init 1.创建项目目录 2.进入目录 3.git初始化 [dirname]为自己取的文件夹名字,例如mkdir myd ...

  8. openjudge2985(数字组合)

    描述 有n个正整数,找出其中和为t(t也是正整数)的可能的组合方式.如:n=5,5个数分别为1,2,3,4,5,t=5:那么可能的组合有5=1+4和5=2+3和5=5三种组合方式.输入输入的第一行是两 ...

  9. 安卓稳定性压测工具_monkey环境搭建(简易)

    1.准备工具: sdk(64位操作系统):http://pan.baidu.com/s/1kV33pll sdk(32位操作系统):http://pan.baidu.com/s/1gfnww87 2. ...

  10. 了解及使用IPV6

    1. 什么是 IPv6 IPv6指互联网协议(IP)第6版.目前大家上网主要使用互联网协议第四版,即IPv4. 在全球互联网高度发展的今天,IPv4 地址资源已经枯竭,互联网正在经历从IPv4网络向I ...