之前都是看别人写博客,自己没有写博客的习惯.在工作的过程中,总是会碰到许多的技术问题.有很多时候想记录下来,后面一直有许多的问题等着解决.总想着等系统完成了,再回头总结下.往往结果就把这事抛到脑后了.

总觉得不能一直这样哈.今天简单记一下吧.有表达不清楚的地方,多多包涵.

最近在研究.net orm框架.想开发一套更好用的Orm框架.碰到一个Expression合并的问题.别嫌轮子多.

一.需求情况描述

需要更新部分数据的时候,可能前端传回的只有部分字段的数据.而更新的时候,需要设置更新人,更新日期等.

举个栗子来说:

现在有一个预约信息表

前端需要修改数据内容如下,我们暂且叫表达A

var exp = ExpressionHelper.CreateExpression<AppointmentDto>(a => new {
a.Id,
a.PatientName,
a.PatientNamePy,
a.IdentityCardNumber,
a.Birthday,
a.PatientAge,
a.PatientSex,
a.PatientPhone,
a.Address
});

而写入数据库的时候需要添加更新人,更新时间.LastUpdateUserId和UpdateTime.

于是我们便又多了一个lambda表达式,我们叫它表达式B

var exp = ExpressionHelper.CreateExpression<AppointmentDto>(a => new {
a.Id,
a.PatientName,
a.PatientNamePy,
a.IdentityCardNumber,
a.Birthday,
a.PatientAge,
a.PatientSex,
a.PatientPhone,
a.Address,
a.LastUpdateUserId,
a.UpdateTime
});

这里说下ExpressionHelper.CreateExpression<T>方法,只是一个为了缩减代码长度而写的方法.输入的lambda表达式原样返回了.

外面不用写好长的类型了.Expression这个类型平时不用.写外面看着眼晕.  Expression<Func<AppointmentDto, object>> exp1 = a => new {a.Id,a.PatientName};

/// <summary>
/// 转换Expr
/// 在外面调用时可以使用var以减少代码长度
/// </summary>
/// <param name="expr"></param>
/// <returns></returns>
public static Expression<Func<T, object>> CreateExpression<T>(Expression<Func<T, object>> expr)
{
return expr;
}

所以此处,使用var可以看起来更整洁.但并不推荐在正常情况下使用var.

个人觉得使用var让代码可维护性降低.读起来真的是头疼.之前在维护一个比较大的系统的时候,公司的主要项目,缺少项目文档,代码里面也基本上没啥注释.而且又清一色的var,每个方法返回的是啥类型?你得去方法那边看去.看着真是恼火,又不得不去一点一点的改.都改成相应的类型后,看着就清爽多了.看一眼,流程就基本上能明白大概.所以,var在C#这种强类型语言里,能不用就别用了.

上面就当是发牢骚了.我们回到正题.

我们看到表达式B比表达式A只多了两个字段.大多数代码都是重复的.而且,两个lambda表达式严重的加长了代码行数.几个这样的表达式下来,这个类就到了几百行了.

对于喜欢简洁,简单的我来说,类一大了我就头疼.那咋整?要是有办法将这两个表达式简化处理一下就好了.将表达式A加上一个短的表达式,来实现表达式B呢.

比如实现 var exprB = exprA.Add(a => new { a.PatientPhone });

So,开始捯饬...

二.解决方法

因为这个合并表达式的方法是在个人系统内部使用满足我定制的Orm的类名称需求

所以定义了一个新的Expression表达式类型NewObjectExpression来处理

     /// <summary>
/// New Object Expression
/// 合并NewExpression使用.
/// </summary>
public class NewObjectExpression : Expression, IArgumentProvider
{
private IList<Expression> arguments; /// <summary>
/// 构造方法
/// </summary>
/// <param name="constructor"></param>
/// <param name="arguments"></param>
/// <param name="members"></param>
internal NewObjectExpression(ConstructorInfo constructor, IList<Expression> arguments, List<MemberInfo> members)
{
this.Constructor = constructor;
this.arguments = arguments;
this.Members = members; if (members != null)
{
List<string> nameList = members.Select(member => member.Name).ToList();
for (int i = ; i < nameList.Count; i++)
{
if (!string.IsNullOrEmpty(ExpressionString))
{
ExpressionString += "," + nameList[i];
}
else
{
ExpressionString = nameList[i];
}
}
}
} /// <summary>
/// Gets the static type of the expression that this <see cref="Expression" /> represents. (Inherited from <see cref="Expression"/>.)
/// </summary>
/// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
public override Type Type
{
get { return Constructor.DeclaringType; }
} /// <summary>
/// Returns the node type of this <see cref="Expression" />. (Inherited from <see cref="Expression" />.)
/// </summary>
/// <returns>The <see cref="ExpressionType"/> that represents this expression.</returns>
public sealed override ExpressionType NodeType
{
get { return ExpressionType.New; }
} /// <summary>
/// Gets the called constructor.
/// </summary>
public ConstructorInfo Constructor { get; } /// <summary>
/// Gets the arguments to the constructor.
/// </summary>
public ReadOnlyCollection<Expression> Arguments
{
get { return (ReadOnlyCollection<Expression>)arguments; }
} Expression IArgumentProvider.GetArgument(int index)
{
return arguments[index];
} int IArgumentProvider.ArgumentCount
{
get
{
return arguments.Count;
}
} /// <summary>
/// ExpressionString
/// </summary>
public string ExpressionString { get; private set; } = ""; public ConstructorInfo Constructor1 => Constructor; public List<MemberInfo> Members { get; set; } /// <summary>
/// 更新members
/// </summary>
/// <param name="arguments"></param>
/// <param name="members"></param>
/// <returns></returns>
public NewObjectExpression Update(IList<Expression> arguments, List<MemberInfo> members)
{
if (arguments != null)
{
this.arguments = arguments;
}
if (Members != null)
{
this.Members = members;
ExpressionString = "";
List<string> nameList = members.Select(member => member.Name).ToList();
for (int i = ; i < nameList.Count; i++)
{
if (!string.IsNullOrEmpty(ExpressionString))
{
ExpressionString += "," + nameList[i];
}
else
{
ExpressionString = nameList[i];
}
}
}
return this;
}
}

待处理的属性都放到了Members里面.后面解析使用的也是Members.其它方法Copy自NewExpression的源码,可以删了不用.

下面我们来扩展Expression<Func<T, object>>,让Expression<Func<T, object>>拥有Add和Remove属性的方法.

直接上代码,看前两个方法.后面两个方法是扩展Expression<Func<T, bool>>表达式的And和Or.等有回头有空再介绍.

     /// <summary>
/// Expression 扩展
/// </summary>
public static class ExpressionExpand
{
/// <summary>
/// Expression And
/// NewExpression 合并
/// </summary>
/// <param name="expr"></param>
/// <returns></returns>
public static Expression<Func<T, object>> Add<T>(this Expression<Func<T, object>> expr, Expression<Func<T, object>> expandExpr)
{
Expression<Func<T, object>> result = null;
ParameterExpression parameter = Expression.Parameter(typeof(T), "p");
List<MemberInfo> memberInfoList = new List<MemberInfo>();
#region 处理原expr
if (expr.Body is NewExpression)
{ // t=>new{t.Id,t.Name}
NewExpression newExp = expr.Body as NewExpression;
if (newExp.Members != null)
{
memberInfoList = newExp.Members.ToList();
}
}
else if (expr.Body is NewObjectExpression)
{
NewObjectExpression newExp = expr.Body as NewObjectExpression;
if (newExp.Members != null)
{
memberInfoList = newExp.Members.ToList();
}
}
else if (expr.Body is UnaryExpression)
{ //t=>t.Id
UnaryExpression unaryExpression = expr.Body as UnaryExpression;
MemberExpression memberExp = unaryExpression.Operand as MemberExpression;
memberInfoList.Add(memberExp.Member);
}
#endregion #region 处理扩展expr
if (expandExpr.Body is NewExpression)
{ // t=>new{t.Id,t.Name}
NewExpression newExp = expandExpr.Body as NewExpression;
for (int i = ; i < newExp.Members.Count; i++)
{
MemberExpression memberExp = Expression.Property(parameter, newExp.Members[i].Name);
if (!memberInfoList.Any(member => member.Name == newExp.Members[i].Name))
{
memberInfoList.Add(newExp.Members[i]);
}
}
}
else if (expr.Body is NewObjectExpression)
{
NewObjectExpression newExp = expr.Body as NewObjectExpression;
if (newExp.Members != null && newExp.Members.Count > )
{
for (int i = ; i < newExp.Members.Count; i++)
{
MemberExpression memberExp = Expression.Property(parameter, newExp.Members[i].Name);
if (!memberInfoList.Any(member => member.Name == newExp.Members[i].Name))
{
memberInfoList.Add(newExp.Members[i]);
}
}
}
}
else if (expandExpr.Body is UnaryExpression)
{ //t=>t.Id
UnaryExpression unaryExpression = expandExpr.Body as UnaryExpression;
MemberExpression memberExp = unaryExpression.Operand as MemberExpression;
if (!memberInfoList.Any(exp => exp.Name == memberExp.Member.Name))
{
memberInfoList.Add(memberExp.Member);
}
}
#endregion
NewObjectExpression newObjExpression = new NewObjectExpression(typeof(object).GetConstructors()[], null, memberInfoList);
result = Expression.Lambda<Func<T, object>>(newObjExpression, parameter);
return result;
} /// <summary>
/// Expression Remove
/// NewExpression 合并
/// </summary>
/// <param name="expr"></param>
/// <returns></returns>
public static Expression<Func<T, object>> Remove<T>(this Expression<Func<T, object>> expr, Expression<Func<T, object>> expandExpr)
{
Expression<Func<T, object>> result = null;
ParameterExpression parameter = Expression.Parameter(typeof(T), "p");
List<MemberInfo> memberInfoList = new List<MemberInfo>();
List<MemberInfo> removeMemberInfoList = new List<MemberInfo>();
#region 处理原expr
if (expr.Body is NewExpression)
{ // t=>new{t.Id,t.Name}
NewExpression newExp = expr.Body as NewExpression;
if (newExp.Members != null)
{
memberInfoList = newExp.Members.ToList();
}
}
else if (expr.Body is NewObjectExpression)
{
NewObjectExpression newExp = expr.Body as NewObjectExpression;
if (newExp.Members != null)
{
memberInfoList = newExp.Members.ToList();
}
}
else if (expr.Body is UnaryExpression)
{ //t=>t.Id
UnaryExpression unaryExpression = expr.Body as UnaryExpression;
MemberExpression memberExp = unaryExpression.Operand as MemberExpression;
memberInfoList.Add(memberExp.Member);
}
#endregion #region 处理扩展expr
if (expandExpr.Body is NewExpression)
{ // t=>new{t.Id,t.Name}
NewExpression newExp = expandExpr.Body as NewExpression;
for (int i = ; i < newExp.Members.Count; i++)
{
MemberExpression memberExp = Expression.Property(parameter, newExp.Members[i].Name);
if (!removeMemberInfoList.Any(member => member.Name == newExp.Members[i].Name))
{
removeMemberInfoList.Add(newExp.Members[i]);
}
}
}
else if (expr.Body is NewObjectExpression)
{
NewObjectExpression newExp = expr.Body as NewObjectExpression;
if (newExp.Members != null && newExp.Members.Count > )
{
for (int i = ; i < newExp.Members.Count; i++)
{
MemberExpression memberExp = Expression.Property(parameter, newExp.Members[i].Name);
if (!removeMemberInfoList.Any(member => member.Name == newExp.Members[i].Name))
{
removeMemberInfoList.Add(newExp.Members[i]);
}
}
}
}
else if (expandExpr.Body is UnaryExpression)
{ //t=>t.Id
UnaryExpression unaryExpression = expandExpr.Body as UnaryExpression;
MemberExpression memberExp = unaryExpression.Operand as MemberExpression;
if (!memberInfoList.Any(exp => exp.Name == memberExp.Member.Name))
{
removeMemberInfoList.Add(memberExp.Member);
}
}
#endregion for (int i = memberInfoList.Count - ; i >= ; i--)
{
if (removeMemberInfoList.Any(member => member.Name == memberInfoList[i].Name))
{
memberInfoList.Remove(memberInfoList[i]);
}
}
if (memberInfoList.Count <= )
{
throw new System.Exception("Expression Remove Error.All Properties are removed.");
}
NewObjectExpression newObjExpression = new NewObjectExpression(typeof(object).GetConstructors()[], null, memberInfoList);
result = Expression.Lambda<Func<T, object>>(newObjExpression, parameter);
return result;
} /// <summary>
/// Expression And
/// </summary>
/// <param name="expr"></param>
/// <returns></returns>
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr, Expression<Func<T, bool>> expandExpr)
{
Expression<Func<T, bool>> result = Expression.Lambda<Func<T, bool>>(Expression.And(expandExpr.Body, expr.Body), expr.Parameters);
return result;
} /// <summary>
/// Expression And
/// </summary>
/// <param name="expr"></param>
/// <returns></returns>
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr, Expression<Func<T, bool>> expandExpr)
{
Expression<Func<T, bool>> result = Expression.Lambda<Func<T, bool>>(Expression.Or(expandExpr.Body, expr.Body), expr.Parameters);
return result;
}
}

Add方法可处理 NewExpression 类似 t=>new{t.Id,t.Name} , UnaryExpression 类似t=>t.Id,以及我们自定义的NewObjectExpression类型

所以我们在更新数据的时候就可以这么写了:

Dbc.Db.Update(dto, exp.Add(a => a.LastUpdateUserId));
Dbc.Db.Update(dto, exp.Add(a => new { a.LastUpdateUserId, a.UpdateTime }));

在Orm框架内部,解析NewObjectExpression时,解析方法如下

         /// <summary>
/// 通过Lambed Expression获取属性名称
/// </summary>
/// <param name="expr">查询表达式</param>
/// <returns></returns>
public static List<string> GetPiList<T>(Expression<Func<T, object>> expr)
{
List<string> result = new List<string>();
if (expr.Body is NewExpression)
{ // t=>new{t.Id,t.Name}
NewExpression nexp = expr.Body as NewExpression;
if (nexp.Members != null)
{
result = nexp.Members.Select(member => member.Name).ToList();
}
}
else if (expr.Body is NewObjectExpression)
{ // t=>new{t.Id,t.Name}
NewObjectExpression nexp = expr.Body as NewObjectExpression;
if (nexp.Members != null)
{
result = nexp.Members.Select(member => member.Name).ToList();
}
}
else if (expr.Body is UnaryExpression)
{ //t=>t.Id
UnaryExpression uexp = expr.Body as UnaryExpression;
MemberExpression mexp = uexp.Operand as MemberExpression;
result.Add(mexp.Member.Name);
}
else
{
throw new System.Exception("不支持的Select lambda写法");
}
return result;
}

至此,就完成了Expression<Func<T, object>>Add和Remove属性的扩展,Orm可以让代码更简洁.

三.后记

其实在使用新的类NewObjectExpression来解决之前,尝试过其它的许多方式,因为使用.net的类型可以在其它的框架程序中借鉴引用.不必局限在个人框架内部.

NewExpression内部有一些校验,本身Expression<Func<T, object>>是一个匿名类.试过处理NewExpression,以及新建类继承自NewExpression等方式.都没成功.

要是大家有更好的方法欢迎留言告知.希望本文能对大家有所帮助.

Orm框架开发之NewExpression合并问题的更多相关文章

  1. Java注解 框架开发之Java注解的妙用

    原文出处: locality 注解的好处: 1.能够读懂别人写的代码,特别是框架相关的代码. 2.本来可能需要很多配置文件,需要很多逻辑才能实现的内容,就可以使用一个或者多个注解来替代,这样就使得编程 ...

  2. 框架开发之Java注解的妙用

    注解的好处:1.能够读懂别人写的代码,特别是框架相关的代码.2.本来可能需要很多配置文件,需要很多逻辑才能实现的内容,就可以使用一个或者多个注解来替代,这样就使得编程更加简洁,代码更加清晰.3.(重点 ...

  3. iOS开发之SceneKit框架--加载多个模型.dae/.scn文件

    1.通过SCNGeometry或子类SCNParametricGeometry创建 相关链接:iOS开发之SceneKit框架--SCNGeometry.h iOS开发之SceneKit框架--SCN ...

  4. 【转载】浅谈游戏开发之2D手游工具

    浅谈游戏开发之2D手游工具 来源:http://www.gameres.com/459713.html 游戏程序 平台类型: iOS Android  程序设计: 其它  编程语言:   引擎/SDK ...

  5. Asp.net Mvc模块化开发之“部分版本部分模块更新(上线)”

    项目开发从来就不是一个简单的问题.更难的问题是维护其他人开发的项目,并且要修改bug.如果原系统有重大问题还需要重构. 怎么重构系统不是本文探讨的问题,但是重构后如何上线部署和本文关系密切.这个大家可 ...

  6. ORM框架-VB/C#.Net实体代码生成工具(EntitysCodeGenerate)【ECG】4.5

    摘要:VB/C#.Net实体代码生成工具(EntitysCodeGenerate)[ECG]是一款专门为.Net数据库程序开发量身定做的(ORM框架)代码生成工具,所生成的程序代码基于OO.ADO.N ...

  7. JavaEE开发之SpringBoot整合MyBatis以及Thymeleaf模板引擎

    上篇博客我们聊了<JavaEE开发之SpringBoot工程的创建.运行与配置>,从上篇博客的内容我们不难看出SpringBoot的便捷.本篇博客我们继续在上篇博客的基础上来看一下Spri ...

  8. 高效开发之SASS篇 灵异留白事件——图片下方无故留白 你会用::before、::after吗 link 与 @import之对比 学习前端前必知的——HTTP协议详解 深入了解——CSS3新增属性 菜鸟进阶——grunt $(#form :input)与$(#form input)的区别

    高效开发之SASS篇   作为通往前端大神之路的普通的一只学鸟,最近接触了一样稍微高逼格一点的神器,特与大家分享~ 他是谁? 作为前端开发人员,你肯定对css很熟悉,但是你知道css可以自定义吗?大家 ...

  9. Android混合开发之WebViewJavascriptBridge实现JS与java安全交互

    前言: 为了加快开发效率,目前公司一些功能使用H5开发,这里难免会用到Js与Java函数互相调用的问题,这个Android是提供了原生支持的,不过存在安全隐患,今天我们来学习一种安全方式来满足Js与j ...

随机推荐

  1. AngularJs HTTP响应拦截器实现登陆、权限校验

    $httpAngularJS 的 $http 服务允许我们通过发送 HTTP 请求方式与后台进行通信.在某些情况下,我们希望可以俘获所有的请求,并且在将其发送到服务端之前进行操作.还有一些情况是,我们 ...

  2. jqGrid常用属性和方法介绍

    jqGrid API中文手册:http://blog.mn886.net/jqGrid/ 一.jqGrid属性: width:Grid的宽度,如果未设置,则宽度应为所有列宽的之和:如果设置了宽度,则每 ...

  3. Cognos开发图表乱码问题

    在此之前提到过在利用TR建模导入IQD数据源的时候遇到乱码的一种解决方案: http://www.cnblogs.com/wxjnew/p/3374029.html 今天说的是在RS中开发新报表的时候 ...

  4. [Javascript] Ternary Conditionals

    /** Ternary Conditionals */ // //**Bad** // var isArthur = false; var weapon; if(isArthur){ weapon = ...

  5. 免费的HTML模板引导 - lonely

    ​在线演示 本地下载 今天和大家分享另一款模板-Lonely.它可以被用在一些个人或者类似简单一些的网站上,动画效果的滚动非常特别!

  6. ArcEngine中的缩放地图

    在ArcEngine地图操作中,缩放地图的功能经常用到,这里做一个小结. 缩放地图一般可分为以下几种情况: 1.缩放地图:与放大地图相对,一般是手动绘制区域或固定比例缩放,可调用命令或Expand函数 ...

  7. java.lang.NoClassDefFoundError: com.baidu.mapapi.BMapManager

    解决方案:一.右击项目->properties->Java Build Path->Order and Export,在需要引用的包前面打勾.二.Project->Clean. ...

  8. bzoj3675【APIO2014】序列切割

    3675: [Apio2014]序列切割 Time Limit: 40 Sec  Memory Limit: 128 MB Submit: 1468  Solved: 607 [Submit][Sta ...

  9. TCP相关面试题总结

    1.TCP三次握手过程 wireshark抓包为:(wireshark会将seq序号和ACK自己主动显示为相对值) 1)主机A发送标志syn=1,随机产生seq =1234567的数据包到server ...

  10. 1z0-052 q209_2

    2: View the Exhibit to examine the output produced by the following query at three different times s ...