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

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

最近在研究.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. Reinvent the Wheel Often

    Reinvent the Wheel Often Jason P. Sage Just use something that exists-it's silly to reinvent the whe ...

  2. Appium Python 五:元素定位

    总结 单个元素定位: driver.find_element_by_accessibility_id(id) driver.find_element_by_android_uiautomator(ui ...

  3. 给你一个能生成1到5随机数的函数,用它写一个函数生成1到7的随机数。 (即,使用函数rand5()来实现函数rand7())

    给你一个能生成1到5随机数的函数,用它写一个函数生成1到7的随机数. (即,使用函数rand5()来实现函数rand7()). 解答 rand5可以随机生成1,2,3,4,5:rand7可以随机生成1 ...

  4. jquery.datepicker、jquery.validate、jquery.uploadify冲突解决

    Jquery 1.11.2 Jquery.validate 1.13.1 Jquery.Uploadify 3.2(flash版) Jquery.DatePicker 用的是Jquery-ui 1.1 ...

  5. vue 虚拟dom

    https://segmentfault.com/a/1190000008291645 一个VNode的实例对象包含了以下属性 tag: 当前节点的标签名 data: 当前节点的数据对象 VNode可 ...

  6. Memcachedclient-XMemcached使用

    一. XMemcached 简单介绍 XMemcached 是一个新 java memcached client . 或许你还不知道 memcached 是什么?能够先看看这里.简单来说, Memca ...

  7. jquery选中以什么开头的元素

    $("[id^=percent]").size() ^=:表示以什么开头 $=:表示以什么结尾 ~=:表示包含什么 id:表示按id选择

  8. 修改终端下vim的PopupMenu选种项的背景颜色

    我平常比较喜欢使用终端下的 VIM,最方便的就是随时可以使用ctrl+z切换到终端下执行命令, 然后再通过fg切换回 VIM.如果再有个透明效果,那就更赞了.不过最近换了一个配色ron 后, 有个比较 ...

  9. uva 699 The Falling Leaves(建二叉树同一时候求和)

    本来看着挺难的.大概是由于我多瞟了一眼题解,瞬间认为简单多了.做题就得这样,多自己想想.如今是 多校联赛,然而我并不会做. .. .慢慢来,一直在努力. 分析: 题上说了做多不会超过80行.所以能够开 ...

  10. [Done]BTrace使用小记

    项目上线,发现集群机器每隔1小时会准时进行fullgc,JMC中显示了System.gc()触发了fullgc: 代码搜了一下自然是搜不到显示调用这个代码的(谁会这么干!拍死!!!)依赖jar包中到是 ...