Orm框架开发之NewExpression合并问题
之前都是看别人写博客,自己没有写博客的习惯.在工作的过程中,总是会碰到许多的技术问题.有很多时候想记录下来,后面一直有许多的问题等着解决.总想着等系统完成了,再回头总结下.往往结果就把这事抛到脑后了.
总觉得不能一直这样哈.今天简单记一下吧.有表达不清楚的地方,多多包涵.
最近在研究.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合并问题的更多相关文章
- Java注解 框架开发之Java注解的妙用
原文出处: locality 注解的好处: 1.能够读懂别人写的代码,特别是框架相关的代码. 2.本来可能需要很多配置文件,需要很多逻辑才能实现的内容,就可以使用一个或者多个注解来替代,这样就使得编程 ...
- 框架开发之Java注解的妙用
注解的好处:1.能够读懂别人写的代码,特别是框架相关的代码.2.本来可能需要很多配置文件,需要很多逻辑才能实现的内容,就可以使用一个或者多个注解来替代,这样就使得编程更加简洁,代码更加清晰.3.(重点 ...
- iOS开发之SceneKit框架--加载多个模型.dae/.scn文件
1.通过SCNGeometry或子类SCNParametricGeometry创建 相关链接:iOS开发之SceneKit框架--SCNGeometry.h iOS开发之SceneKit框架--SCN ...
- 【转载】浅谈游戏开发之2D手游工具
浅谈游戏开发之2D手游工具 来源:http://www.gameres.com/459713.html 游戏程序 平台类型: iOS Android 程序设计: 其它 编程语言: 引擎/SDK ...
- Asp.net Mvc模块化开发之“部分版本部分模块更新(上线)”
项目开发从来就不是一个简单的问题.更难的问题是维护其他人开发的项目,并且要修改bug.如果原系统有重大问题还需要重构. 怎么重构系统不是本文探讨的问题,但是重构后如何上线部署和本文关系密切.这个大家可 ...
- ORM框架-VB/C#.Net实体代码生成工具(EntitysCodeGenerate)【ECG】4.5
摘要:VB/C#.Net实体代码生成工具(EntitysCodeGenerate)[ECG]是一款专门为.Net数据库程序开发量身定做的(ORM框架)代码生成工具,所生成的程序代码基于OO.ADO.N ...
- JavaEE开发之SpringBoot整合MyBatis以及Thymeleaf模板引擎
上篇博客我们聊了<JavaEE开发之SpringBoot工程的创建.运行与配置>,从上篇博客的内容我们不难看出SpringBoot的便捷.本篇博客我们继续在上篇博客的基础上来看一下Spri ...
- 高效开发之SASS篇 灵异留白事件——图片下方无故留白 你会用::before、::after吗 link 与 @import之对比 学习前端前必知的——HTTP协议详解 深入了解——CSS3新增属性 菜鸟进阶——grunt $(#form :input)与$(#form input)的区别
高效开发之SASS篇 作为通往前端大神之路的普通的一只学鸟,最近接触了一样稍微高逼格一点的神器,特与大家分享~ 他是谁? 作为前端开发人员,你肯定对css很熟悉,但是你知道css可以自定义吗?大家 ...
- Android混合开发之WebViewJavascriptBridge实现JS与java安全交互
前言: 为了加快开发效率,目前公司一些功能使用H5开发,这里难免会用到Js与Java函数互相调用的问题,这个Android是提供了原生支持的,不过存在安全隐患,今天我们来学习一种安全方式来满足Js与j ...
随机推荐
- [5] 柱台(Cylinder)图形的生成算法
顶点数据的生成 bool YfBuildCylinderVertices ( Yreal topRadius, Yreal bottomRadius, Yreal height, Yuint slic ...
- CURL库在C++程序中的运用浅析
最近由于要做一个爬虫项目,要对很多网站进行爬取,所以一直都在看这方面的文章.在翻阅了很多资料后,下载了一个curl库,着实对项目有了很大的帮助. 一.LibCurl基本编程框架 二.一些基本的函数 三 ...
- go语言基础之复合类型
1.分类 类型 名称 长度 默认值 说明 pointer 指针 nil array 数组 0 slice 切片 nil 引⽤类型 map 字典 nil 引⽤类型 struct 结构体 2.指针 指针是 ...
- Python 批量修改图片格式和尺寸
公司的一个项目要求把所有4096x4096的图片全部转化成2048x2048的图片,这种批量转换图片大小的软件网上很多,我的同事原来使用的美图看看的批量转换,但是稍微有点麻烦,每次还需要指定要转换的图 ...
- GoLang中如何使用多参数属性传参
我们常常因为传入的参数不确定而头疼不已,golang 为我们提供了接入多值参数用于解决这个问题.但是一般我们直接写已知代码即所有的值都知道一个一个塞进去就好了,但是绝大部分我们是得到用户的大量输入想通 ...
- ubuntu移植jsoncpp到Android平台(转)
NDK开发模块的时候,如果涉及到网络请求,类似json数据传递的时候,有现成的第三方json库可以移植,后台C++开发中使用的比较多的是jsoncpp,今天记录一下jsoncpp移植到Android平 ...
- 【安卓】给gallery内"控件"挂载事件,滑动后抬起手指时也触发事件(滑动时不应触发)的解决、!
思路: 1.gallery内控件挂载事件(如:onClickListener)的方法类似listview,可直接在baseAdapter.getView内给控件挂载(详细方法百度). 2.貌似没问题, ...
- R语言中数据结构
R语言还是有点古老感觉,数据结构没有Python中那么好用.以下简单总结一下R语言中经常使用的几个数据结构. 向量: R中的向量能够理解为一维的数组,每一个元素的mode必须同样,能够用c(x:y)进 ...
- SDUT 1157-小鼠迷宫问题(BFS&DFS)
小鼠迷宫问题 nid=24#time" title="C.C++.go.haskell.lua.pascal Time Limit1500ms Memory Limit 65536 ...
- DIV+CSS布局重新学习之css控制ul li实现2级菜单
竖状菜单: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w ...