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

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

最近在研究.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. 附2 hystrix详述(2)- 配置

    一.hystrix在生产中的建议 1.保持timeout的默认值(1000ms),除非需要修改(其实通常会修改) 2.保持threadpool的的线程数为10个,除非需要更多 3.依赖标准的报警和监控 ...

  2. C#操作AD及Exchange Server总结(一)

    这篇博客的目的:根据亲身项目经历,总结对AD及Exchange Server的操作,包括新建AD用户,设置密码,为AD用户创建邮箱等. 本文完全原创,转载请说明出处,希望对大家有用. 文档目录: 测试 ...

  3. 如何查看ETW Trace?

    三种方法: LogParser Tractrpt - C:\Windows\System32 SvcTraceViewer.exe   后面的链接中有使用的详细步骤.   参考资料 ========= ...

  4. js复制当前url地址解决浏览器兼容

    1.网上搜索的js复制链接代码,好像只能支持ie浏览器,其他浏览器不支持, 案例: var url=12; if(window.clipboardData){                   wi ...

  5. Android -- 跳转应用市场评分

    Code Uri uri = Uri.parse("market://details?id="+getPackageName()); Intent intent = new Int ...

  6. IDA 远程调试 Android so

      1.把ida 目录下android_server 传到android 目录中如:adb push  android_server /data/local/tmp/adb shell 进入模拟器cd ...

  7. 动态改变UIPopupList选项(NGUI)

    NGUI的UIPopupList 可以通过修改items属性来动态改变菜单选项: public class popListvahnge : MonoBehaviour { public UIPopup ...

  8. 解决Sublime包管理package control 报错 There are no packages available for installation

    解决Sublime包管理package control 报错 There are no packages available for installation 真的是哔了狗了,要不是我机智的重新安装了 ...

  9. Mac Finder中如何复制当前完整路径

    1.拖到命令行 2.在Finder中command+i 会弹出详细信息,然后[位置]处进行 copy 3.利用Automator,添加一个服务的快捷键. 转自:http://q.cnblogs.com ...

  10. Office EXCEL 不用VB,你也可以制作自己的Excel菜单!

    还记得这个讨厌的VB吗?为了做一个COM插件,生成一个DLL,麻烦一大堆.其实我们想要的仅仅是把自己写的宏封装一下,更好的调用而已. 打开工具,自定义,在命令菜单中选择新菜单,然后拖放右侧的新菜单到顶 ...