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等方式.都没成功.

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

  • 相关阅读:
    一行代码更改博客园皮肤
    fatal: refusing to merge unrelated histories
    使用 netcat 传输大文件
    linux 命令后台运行
    .net core 使用 Nlog 配置文件
    .net core 使用 Nlog 集成 exceptionless 配置文件
    Mysql不同字符串格式的连表查询
    Mongodb between 时间范围
    VS Code 使用 Debugger for Chrome 调试vue
    css权重说明
  • 原文地址:https://www.cnblogs.com/arthur3k/p/10244293.html
Copyright © 2011-2022 走看看