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

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

最近在研究.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. 第十一章 AtomicInteger源码解析

    1.原子类 可以实现一些原子操作 基于CAS 下面就以AtomicInteger为例. 2.AtomicInteger 在没有AtomicInteger之前,对于一个Integer的线程安全操作,是需 ...

  2. JDBC上关于数据库中多表操作一对多关系和多对多关系的实现方法

    黑马程序员 我们知道,在设计一个Javabean的时候,要把这些BEAN 的数据存放在数据库中的表结构,然而这些数据库中的表直接又有些特殊的关系,例如员工与部门直接有一对多的关系,学生与老师直接又多对 ...

  3. 深入理解VUE样式style层次分析

    刚开始使用vue的时候容易被里面的样式搞懵: 样式可以在main.js中引入,在模块js文件中引入,在组件中的style标签引入,在组件中的script标签引入,还可以在index.html的body ...

  4. 模态框在IE下的问题,即position:fixed在IE下不兼容的处理方式

    项目中遇到的问题,模态框在IE下总出现如图所示双层遮罩框,经排查发现是由于bootstrap里写的modal的样式里position:fixed不兼容IE的原因,导致铺不满整个窗口. 解决方案:在项目 ...

  5. 锐浪报表 导出 PDF ANSI码 乱码 问题解决

    锐浪 报表 导出PDF时如果 ANSI 码 打勾了会乱码,能将这个选项默认不打勾吗 //在报表导出事件脚本里写脚本,可实现导出控制Sender.AsE2PDFOption.AnsiTextMode=0 ...

  6. 如何利用Framework模型生成IQD文件

    很多Cognos的新手在接触Transform建模的时候对于iqd文件都有一种朦胧的感觉,当然也不必去死记硬别它的格式,下面我们就来说一下如何用Framework工具来生成iqd文件. 1:打开fra ...

  7. C++ RegCreateKeyEx成功了,但是注册表并没有这一项

    C++ - RegCreateKeyEx success but without result Could anybody tell me what's wrong is with this code ...

  8. 【C#】利用JMail发送邮件

    有用到需要发送帐号激活邮件,利用Jmail去做蛮简单的,先记录下: 1.首先到Jmail官网下载对应的版本,解压后安装(Jmail 4.4 免费版). 2.到安装目录就可以找到jmail.dll文件, ...

  9. .NET破解之迅捷PDF转换器(续)

    在以前的博文<.NET破解之迅捷PDF转换器>中使用了暴力破解的方法,现在软件版本从5.0升级到6.3,所以也尝试用新的方法. 方法一:暴力破解法 如往常一样,查找搜索到关键的函数,即Is ...

  10. Oracle 之 表新增字段后修改字段顺序

    工作中遇到:在为一个表新增字段后,新增字段在最后,想调整新增字段的位置. 1.原始方法: --新建临时表以存储正确的顺序 create table A_2 as select (column1,col ...