zoukankan      html  css  js  c++  java
  • 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来处理

      1     /// <summary>
      2     /// New Object Expression
      3     /// 合并NewExpression使用.
      4     /// </summary>
      5     public class NewObjectExpression : Expression, IArgumentProvider
      6     {
      7         private IList<Expression> arguments;
      8 
      9         /// <summary>
     10         /// 构造方法
     11         /// </summary>
     12         /// <param name="constructor"></param>
     13         /// <param name="arguments"></param>
     14         /// <param name="members"></param>
     15         internal NewObjectExpression(ConstructorInfo constructor, IList<Expression> arguments, List<MemberInfo> members)
     16         {
     17             this.Constructor = constructor;
     18             this.arguments = arguments;
     19             this.Members = members;
     20 
     21             if (members != null)
     22             {
     23                 List<string> nameList = members.Select(member => member.Name).ToList();
     24                 for (int i = 0; i < nameList.Count; i++)
     25                 {
     26                     if (!string.IsNullOrEmpty(ExpressionString))
     27                     {
     28                         ExpressionString += "," + nameList[i];
     29                     }
     30                     else
     31                     {
     32                         ExpressionString = nameList[i];
     33                     }
     34                 }
     35             }
     36         }
     37 
     38         /// <summary>
     39         /// Gets the static type of the expression that this <see cref="Expression" /> represents. (Inherited from <see cref="Expression"/>.)
     40         /// </summary>
     41         /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
     42         public override Type Type
     43         {
     44             get { return Constructor.DeclaringType; }
     45         }
     46 
     47         /// <summary>
     48         /// Returns the node type of this <see cref="Expression" />. (Inherited from <see cref="Expression" />.)
     49         /// </summary>
     50         /// <returns>The <see cref="ExpressionType"/> that represents this expression.</returns>
     51         public sealed override ExpressionType NodeType
     52         {
     53             get { return ExpressionType.New; }
     54         }
     55 
     56         /// <summary>
     57         /// Gets the called constructor.
     58         /// </summary>
     59         public ConstructorInfo Constructor { get; }
     60 
     61         /// <summary>
     62         /// Gets the arguments to the constructor.
     63         /// </summary>
     64         public ReadOnlyCollection<Expression> Arguments
     65         {
     66             get { return (ReadOnlyCollection<Expression>)arguments; }
     67         }
     68 
     69         Expression IArgumentProvider.GetArgument(int index)
     70         {
     71             return arguments[index];
     72         }
     73 
     74         int IArgumentProvider.ArgumentCount
     75         {
     76             get
     77             {
     78                 return arguments.Count;
     79             }
     80         }
     81         
     82         /// <summary>
     83         /// ExpressionString
     84         /// </summary>
     85         public string ExpressionString { get; private set; } = "";
     86 
     87         public ConstructorInfo Constructor1 => Constructor;
     88 
     89         public List<MemberInfo> Members { get; set; }
     90 
     91         /// <summary>
     92         /// 更新members
     93         /// </summary>
     94         /// <param name="arguments"></param>
     95         /// <param name="members"></param>
     96         /// <returns></returns>
     97         public NewObjectExpression Update(IList<Expression> arguments, List<MemberInfo> members)
     98         {
     99             if (arguments != null)
    100             {
    101                 this.arguments = arguments;
    102             }
    103             if (Members != null)
    104             {
    105                 this.Members = members;
    106                 ExpressionString = "";
    107                 List<string> nameList = members.Select(member => member.Name).ToList();
    108                 for (int i = 0; i < nameList.Count; i++)
    109                 {
    110                     if (!string.IsNullOrEmpty(ExpressionString))
    111                     {
    112                         ExpressionString += "," + nameList[i];
    113                     }
    114                     else
    115                     {
    116                         ExpressionString = nameList[i];
    117                     }
    118                 }                
    119             }
    120             return this;
    121         }
    122     }
    View Code

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

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

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

      1     /// <summary>
      2     /// Expression 扩展
      3     /// </summary>
      4     public static class ExpressionExpand
      5     {
      6         /// <summary>
      7         /// Expression And 
      8         /// NewExpression 合并
      9         /// </summary>
     10         /// <param name="expr"></param>
     11         /// <returns></returns>
     12         public static Expression<Func<T, object>> Add<T>(this Expression<Func<T, object>> expr, Expression<Func<T, object>> expandExpr)
     13         {
     14             Expression<Func<T, object>> result = null;
     15             ParameterExpression parameter = Expression.Parameter(typeof(T), "p");
     16             List<MemberInfo> memberInfoList = new List<MemberInfo>();
     17             #region 处理原expr
     18             if (expr.Body is NewExpression)
     19             {   // t=>new{t.Id,t.Name}
     20                 NewExpression newExp = expr.Body as NewExpression;
     21                 if (newExp.Members != null)
     22                 {
     23                     memberInfoList = newExp.Members.ToList();
     24                 }
     25             }
     26             else if (expr.Body is NewObjectExpression)
     27             {
     28                 NewObjectExpression newExp = expr.Body as NewObjectExpression;
     29                 if (newExp.Members != null)
     30                 {
     31                     memberInfoList = newExp.Members.ToList();
     32                 }
     33             }
     34             else if (expr.Body is UnaryExpression)
     35             {   //t=>t.Id
     36                 UnaryExpression unaryExpression = expr.Body as UnaryExpression;
     37                 MemberExpression memberExp = unaryExpression.Operand as MemberExpression;
     38                 memberInfoList.Add(memberExp.Member);
     39             }
     40             #endregion
     41 
     42             #region 处理扩展expr
     43             if (expandExpr.Body is NewExpression)
     44             {   // t=>new{t.Id,t.Name}
     45                 NewExpression newExp = expandExpr.Body as NewExpression;
     46                 for (int i = 0; i < newExp.Members.Count; i++)
     47                 {
     48                     MemberExpression memberExp = Expression.Property(parameter, newExp.Members[i].Name);
     49                     if (!memberInfoList.Any(member => member.Name == newExp.Members[i].Name))
     50                     {
     51                         memberInfoList.Add(newExp.Members[i]);
     52                     }
     53                 }
     54             }
     55             else if (expr.Body is NewObjectExpression)
     56             {
     57                 NewObjectExpression newExp = expr.Body as NewObjectExpression;
     58                 if (newExp.Members != null && newExp.Members.Count > 0)
     59                 {
     60                     for (int i = 0; i < newExp.Members.Count; i++)
     61                     {
     62                         MemberExpression memberExp = Expression.Property(parameter, newExp.Members[i].Name);
     63                         if (!memberInfoList.Any(member => member.Name == newExp.Members[i].Name))
     64                         {
     65                             memberInfoList.Add(newExp.Members[i]);
     66                         }
     67                     }
     68                 }
     69             }
     70             else if (expandExpr.Body is UnaryExpression)
     71             {   //t=>t.Id
     72                 UnaryExpression unaryExpression = expandExpr.Body as UnaryExpression;
     73                 MemberExpression memberExp = unaryExpression.Operand as MemberExpression;
     74                 if (!memberInfoList.Any(exp => exp.Name == memberExp.Member.Name))
     75                 {
     76                     memberInfoList.Add(memberExp.Member);
     77                 }
     78             }
     79             #endregion
     80             NewObjectExpression newObjExpression = new NewObjectExpression(typeof(object).GetConstructors()[0], null, memberInfoList);
     81             result = Expression.Lambda<Func<T, object>>(newObjExpression, parameter);
     82             return result;
     83         }
     84 
     85         /// <summary>
     86         /// Expression Remove 
     87         /// NewExpression 合并
     88         /// </summary>
     89         /// <param name="expr"></param>
     90         /// <returns></returns>
     91         public static Expression<Func<T, object>> Remove<T>(this Expression<Func<T, object>> expr, Expression<Func<T, object>> expandExpr)
     92         {
     93             Expression<Func<T, object>> result = null;
     94             ParameterExpression parameter = Expression.Parameter(typeof(T), "p");
     95             List<MemberInfo> memberInfoList = new List<MemberInfo>();
     96             List<MemberInfo> removeMemberInfoList = new List<MemberInfo>();
     97             #region 处理原expr
     98             if (expr.Body is NewExpression)
     99             {   // t=>new{t.Id,t.Name}
    100                 NewExpression newExp = expr.Body as NewExpression;
    101                 if (newExp.Members != null)
    102                 {
    103                     memberInfoList = newExp.Members.ToList();
    104                 }
    105             }
    106             else if (expr.Body is NewObjectExpression)
    107             {
    108                 NewObjectExpression newExp = expr.Body as NewObjectExpression;
    109                 if (newExp.Members != null)
    110                 {
    111                     memberInfoList = newExp.Members.ToList();
    112                 }
    113             }
    114             else if (expr.Body is UnaryExpression)
    115             {   //t=>t.Id
    116                 UnaryExpression unaryExpression = expr.Body as UnaryExpression;
    117                 MemberExpression memberExp = unaryExpression.Operand as MemberExpression;
    118                 memberInfoList.Add(memberExp.Member);
    119             }
    120             #endregion
    121 
    122             #region 处理扩展expr
    123             if (expandExpr.Body is NewExpression)
    124             {   // t=>new{t.Id,t.Name}
    125                 NewExpression newExp = expandExpr.Body as NewExpression;
    126                 for (int i = 0; i < newExp.Members.Count; i++)
    127                 {
    128                     MemberExpression memberExp = Expression.Property(parameter, newExp.Members[i].Name);
    129                     if (!removeMemberInfoList.Any(member => member.Name == newExp.Members[i].Name))
    130                     {
    131                         removeMemberInfoList.Add(newExp.Members[i]);
    132                     }
    133                 }
    134             }
    135             else if (expr.Body is NewObjectExpression)
    136             {
    137                 NewObjectExpression newExp = expr.Body as NewObjectExpression;
    138                 if (newExp.Members != null && newExp.Members.Count > 0)
    139                 {
    140                     for (int i = 0; i < newExp.Members.Count; i++)
    141                     {
    142                         MemberExpression memberExp = Expression.Property(parameter, newExp.Members[i].Name);
    143                         if (!removeMemberInfoList.Any(member => member.Name == newExp.Members[i].Name))
    144                         {
    145                             removeMemberInfoList.Add(newExp.Members[i]);
    146                         }
    147                     }
    148                 }
    149             }
    150             else if (expandExpr.Body is UnaryExpression)
    151             {   //t=>t.Id
    152                 UnaryExpression unaryExpression = expandExpr.Body as UnaryExpression;
    153                 MemberExpression memberExp = unaryExpression.Operand as MemberExpression;
    154                 if (!memberInfoList.Any(exp => exp.Name == memberExp.Member.Name))
    155                 {
    156                     removeMemberInfoList.Add(memberExp.Member);
    157                 }
    158             }
    159             #endregion
    160 
    161             for (int i = memberInfoList.Count - 1; i >= 0; i--)
    162             {
    163                 if (removeMemberInfoList.Any(member => member.Name == memberInfoList[i].Name))
    164                 {
    165                     memberInfoList.Remove(memberInfoList[i]);
    166                 }
    167             }
    168             if (memberInfoList.Count <= 0)
    169             {
    170                 throw new System.Exception("Expression Remove Error.All Properties are removed.");
    171             }
    172             NewObjectExpression newObjExpression = new NewObjectExpression(typeof(object).GetConstructors()[0], null, memberInfoList);
    173             result = Expression.Lambda<Func<T, object>>(newObjExpression, parameter);
    174             return result;
    175         }
    176 
    177         /// <summary>
    178         /// Expression And
    179         /// </summary>
    180         /// <param name="expr"></param>
    181         /// <returns></returns>
    182         public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr, Expression<Func<T, bool>> expandExpr)
    183         {
    184             Expression<Func<T, bool>> result = Expression.Lambda<Func<T, bool>>(Expression.And(expandExpr.Body, expr.Body), expr.Parameters);
    185             return result;
    186         }
    187 
    188         /// <summary>
    189         /// Expression And
    190         /// </summary>
    191         /// <param name="expr"></param>
    192         /// <returns></returns>
    193         public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr, Expression<Func<T, bool>> expandExpr)
    194         {
    195             Expression<Func<T, bool>> result = Expression.Lambda<Func<T, bool>>(Expression.Or(expandExpr.Body, expr.Body), expr.Parameters);
    196             return result;
    197         }
    198     }
    View Code

    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时,解析方法如下

     1         /// <summary>
     2         /// 通过Lambed Expression获取属性名称
     3         /// </summary>
     4         /// <param name="expr">查询表达式</param>
     5         /// <returns></returns>
     6         public static List<string> GetPiList<T>(Expression<Func<T, object>> expr)
     7         {
     8             List<string> result = new List<string>();
     9             if (expr.Body is NewExpression)
    10             {   // t=>new{t.Id,t.Name}
    11                 NewExpression nexp = expr.Body as NewExpression;
    12                 if (nexp.Members != null)
    13                 {
    14                     result = nexp.Members.Select(member => member.Name).ToList();
    15                 }
    16             }
    17             else if (expr.Body is NewObjectExpression)
    18             {   // t=>new{t.Id,t.Name}
    19                 NewObjectExpression nexp = expr.Body as NewObjectExpression;
    20                 if (nexp.Members != null)
    21                 {
    22                     result = nexp.Members.Select(member => member.Name).ToList();
    23                 }
    24             }
    25             else if (expr.Body is UnaryExpression)
    26             {   //t=>t.Id
    27                 UnaryExpression uexp = expr.Body as UnaryExpression;
    28                 MemberExpression mexp = uexp.Operand as MemberExpression;
    29                 result.Add(mexp.Member.Name);
    30             }
    31             else
    32             {
    33                 throw new System.Exception("不支持的Select lambda写法");
    34             }
    35             return result;
    36         }
    View Code

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

    三.后记

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

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

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

  • 相关阅读:
    Junit单元测试
    Stream流方法引用
    Stream流思想和常用方法
    算法
    函数式接口
    Zookeeper理解
    GreenPlum学习之(Share-nothing)架构
    链表反转问题
    KMP算法的java实现
    KMP详解之二
  • 原文地址:https://www.cnblogs.com/arthur3k/p/10244293.html
Copyright © 2011-2022 走看看