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.等有回头有空再介绍.

    /// <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 = 0; 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 > 0)
                {
                    for (int i = 0; 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()[0], 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 = 0; 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 > 0)
                {
                    for (int i = 0; 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 - 1; i >= 0; i--)
            {
                if (removeMemberInfoList.Any(member => member.Name == memberInfoList[i].Name))
                {
                    memberInfoList.Remove(memberInfoList[i]);
                }
            }
            if (memberInfoList.Count <= 0)
            {
                throw new System.Exception("Expression Remove Error.All Properties are removed.");
            }
            NewObjectExpression newObjExpression = new NewObjectExpression(typeof(object).GetConstructors()[0], 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;
        }
    }
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时,解析方法如下

        /// <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;
        }
View Code

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

三.后记

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

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

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

猜你喜欢

转载自www.cnblogs.com/arthur3k/p/10244293.html
今日推荐