zoukankan      html  css  js  c++  java
  • 使用EF构建企业级应用(三)

    使用EF构建企业级应用(三)

    2012-04-10 14:42 by 谢中涞, 880 visits, 收藏编辑

    在前两篇文章中,我们已经实现了基于EF的数据库基本操作基类的构建,以及简单的介绍了如何方便的动态构建排序表达式,在第二篇文章结尾,我们遗漏下来了一个问题:如何方便的构建查询参数(即类似于这样的Expression<TEntity, bool> expression查询表达式)

    在往常的经验中,我们知道在和数据库交互的过程中,查询可能是最复杂的,做过数据持久化封装的同学们可能对这个认识尤为突出,其他原因我们就不细说了, 如何丰富的,易用的构建查询条件这个就有点让人迷惑.

    我们来分析一个常见的查询条件(a>4 and b<3)Or(d>5 and c in(3,4,5)),

    1. 设定:var exp1=a>4 and b<3;  var exp2=d>5 and c in(3,4,5),则上述条件可以表示为 var exp=exp1 or exp2;
    2. 进一步分解exp1:var exp1_1=a>4; var exp1_2=b<3; 则exp1=exp1_1 and var exp_2
    3. 进一步分解exp2:var exp2_1=d>5; var exp2_2=c in (3,4,5); 则exp2=exp2_1 and exp2_2

    有了上面的分解,我们发现其实构建这个查询条件非常符合我们常见的递归算法.下面我们尝试一下用递归的思想来实现这个.我们先大致的画个UML草图image

    为了不和系统下面的Expression 命名混淆,我们这里采用EFExpression作为条件表达式基类名称,

    1. EFExpression<T> 为一个抽象基类,里面主要有一个返回类型为 Expression<Func<T,bool>> GetExpression()的一个抽象方法,其具体实现,我们放在了每一个具体的Expression中去定义.
    2. EmptyEFExpression<T> 表示一个空的查询表达式
    3. BinaryEFExpression<T> 表示一个基于二元条件的查询表达式,如大于,小于,等于 ……
    4. LikeEFExpression<T> 表示一个用于创建”类似于”条件的查询表达式
    5. LogicEFExpression<T> 表示一个逻辑运算的查询表达式,如and or
    6. ….可能还会有其他子类

    1.首先我们试着来编写一下抽象基类EFExpression<T>的实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    /// <summary>
    /// 查询表达式基类
    /// </summary>
    public abstract class EFExpression<T> where T : class
    {
     
        /// <summary>
        /// 获取查询表达式
        /// </summary>
        /// <returns></returns>
        public virtual Expression<Func<T, bool>> GetExpression()
        {
            if (Expression != null)
            {
                var candidateExp = Expression.Parameter(typeof(T), "x");
                var exp = Expression.Lambda<Func<T, bool>>(Expression, candidateExp);
                return exp;
            }
            else
            {
                return null;
            }
        }
     
        /// <summary>
        /// 查询条件对应表达式
        /// </summary>
        protected Expression _Expression { get; set; }
     
        /// <summary>
        /// 参数表达式
        /// </summary>
        public ParameterExpression[] Parameters { get; set; }
     
        /// <summary>
        /// 获取对应的表达式
        /// </summary>
        internal abstract Expression Expression { get; }
     
        /// <summary>
        /// 获取表达式主题
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="P"></typeparam>
        /// <param name="old"></param>
        /// <param name="property"></param>
        /// <returns></returns>
        protected static Expression GetMemberExpression<T, P>(EFExpression<T> old, Expression<Func<T, P>> property)
            where T : class
        {
            if (old.Parameters == null || old.Parameters.Length == 0)
            {
                old.Parameters = property.Parameters.ToArray();
                return property.Body;
            }
     
            ParameterExpressionVisitor visitor = new ParameterExpressionVisitor(old.Parameters[0]);
     
            Expression memberExpr = visitor.ChangeParameter(property.Body);
     
            return memberExpr;
        }
     
        /// <summary>
        /// 获取一个空的表达式
        /// </summary>
        /// <returns></returns>
        public Expression<Func<T, bool>> GetEmptyExpression()
        {
            return (Expression<Func<T, bool>>)(f => true);
        }
     
        /// <summary>
        /// 两个条件进行And运算
        /// </summary>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns></returns>
        public static EFExpression<T> operator &(EFExpression<T> left, EFExpression<T> right)
        {
            return new LogicEFExpression<T>(left, ELogicType.And, right);
        }
        /// <summary>
        /// 两个条件进行Or运行
        /// </summary>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns></returns>
        public static EFExpression<T> operator |(EFExpression<T> left, EFExpression<T> right)
        {
            return new LogicEFExpression<T>(left, ELogicType.Or, right);
        }
    }

    2. 接着我们来看一下如何实现这个BinaryEFExpression<T>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    /// <summary>
        /// 二元运算查询条件
        /// </summary>
        /// <typeparam name="T">查询条件实体类型</typeparam>
        /// <typeparam name="TVal">需要比较的属性类型</typeparam>
        internal class BinanryEFExpression<T, TVal> : EFExpression<T>
            where T : class
            where TVal : IComparable
        {
            /// <summary>
            /// 定义条件的实体属性
            /// </summary>
            private Expression<Func<T, TVal>> property;
     
            /// <summary>
            /// 比较的值
            /// </summary>
            private TVal val;
     
            /// <summary>
            /// 二元运算符
            /// </summary>
            private EBinaryType binaryType;
     
            /// <summary>
            /// 实例化新的二元查询表达式
            /// </summary>
            /// <param name="property">定义条件的实体属性</param>
            /// <param name="binaryType">二元运算符</param>
            /// <param name="val">比较的值</param>
            public BinanryEFExpression(Expression<Func<T, TVal>> property, EBinaryType binaryType, TVal val)
            {
                if (property == null)
                    throw new ArgumentNullException("property");
                //if (val == null && binaryType != EBinaryType.Like)
                //    throw new ArgumentNullException("val");
                this.property = property;
                this.val = val;
                this.binaryType = binaryType;
            }
     
            internal override Expression Expression
            {
                get
                {
                    if (_Expression == null)
                    {
                        var propertyBody = GetMemberExpression(this, property);
     
                        Type type = typeof(TVal);
                        Expression compareVal = Expression.Constant(val);
                        //如果是Nullable类型,则把value转化成Nullable类型
                        if (type.IsNullableType())
                        {
                            compareVal = Expression.Convert(compareVal, type);
                        }
                        Expression tempExp = null;
                        switch (binaryType)
                        {
                            case EBinaryType.Equal:
                                tempExp = Expression.Equal(propertyBody, compareVal);
                                break;
                            case EBinaryType.GreaterThan:
                                tempExp = Expression.GreaterThan(propertyBody, compareVal);
                                break;
                            case EBinaryType.GreaterThanOrEqual:
                                tempExp = Expression.GreaterThanOrEqual(propertyBody, compareVal);
                                break;
                            case EBinaryType.LessThan:
                                tempExp = Expression.LessThan(propertyBody, compareVal);
                                break;
                            case EBinaryType.LessThanOrEqual:
                                tempExp = Expression.LessThanOrEqual(propertyBody, compareVal);
                                break;
                            case EBinaryType.NotEqual:
                                tempExp = Expression.NotEqual(propertyBody, compareVal);
                                break;
                            default:
                                break;
                        }
                        _Expression = tempExp;
                    }
                    return _Expression;
                }
            }
        }

    3.我们在来看一下关于表达式逻辑运算的LogicEFExpression<T> 类又是如何实现的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    /// <summary>
       /// 带有逻辑运算的查询表达式
       /// </summary>
       /// <typeparam name="T"></typeparam>
       public class LogicEFExpression<T> : EFExpression<T> where T : class
       {
           private EFExpression<T> left;
           private EFExpression<T> right;
           private ELogicType logicType;
     
           /// <summary>
           /// 实例化新的逻辑运算查询表达式
           /// </summary>
           /// <param name="left"></param>
           /// <param name="logicType">逻辑运算类型</param>
           /// <param name="right"></param>
           public LogicEFExpression(EFExpression<T> left, ELogicType logicType, EFExpression<T> right)
           {
               if (left == null || right == null)
                   throw new ArgumentNullException("left 和 right 不能同时为空");
               this.left = left;
               this.right = right;
               this.logicType = logicType;
           }
     
           public override Expression<Func<T, bool>> GetExpression()
           {
               if (left == null)
                   return right.GetExpression();
               else if (right == null)
                   return left.GetExpression();
               else
               {
                   //判断进行运算的两个条件是否为空
                   if (left is EmptyEFExpression<T> && right is EmptyEFExpression<T>)
                       return left.GetExpression();
                   else if (left is EmptyEFExpression<T>)
                       return right.GetExpression();
                   else if (right is EmptyEFExpression<T>)
                       return left.GetExpression();
     
                   var leftExp = left.GetExpression();
                   var rightExp = right.GetExpression();
                   Expression<Func<T, bool>> exp = null;
     
                   if (leftExp == null && rightExp == null)
                       return new EmptyEFExpression<T>().GetExpression();
                   else
                   {
                       if (leftExp == null)
                           return rightExp;
                       else if (rightExp == null)
                           return leftExp;
                       else
                       {
                           switch (logicType)
                           {
                               case ELogicType.And:
                                   exp = leftExp.And(rightExp);
                                   break;
                               case ELogicType.Or:
                                   exp = leftExp.Or(rightExp);
                                   break;
                               default:
                                   break;
                           }
                       }
                   }
                   return exp;
               }
           }
     
           internal override Expression Expression
           {
               get
               {
                   return null;
               }
           }
       }
    
    


     

    4.为了使用方便,我们在基类中再添加个静态方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /// <summary>
           /// 构建一个二元查询表达式
           /// </summary>
           /// <typeparam name="TVal">比较值的类型</typeparam>
           /// <param name="property">定义条件的实体属性</param>
           /// <param name="binaryType">运算符类型</param>
           /// <param name="val">比较的值</param>
           /// <returns></returns>
           public static EFExpression<T> CreateBinaryExpression<TVal>(Expression<Func<T, TVal>> property,
               EBinaryType binaryType, TVal val) where TVal : IComparable
           {
               return new BinanryEFExpression<T, TVal>(property, binaryType, val);
           }

    5.有了上面的实现,我们来测试下我们的想法.为了让我们更容易理解,我们假设在一个订单管理环境中,有如下一个需求:请查询满足以下任意一个条件中的订单记录

    1. 订单状态还未送货且金额>50W的
    2. 订单状态为送货中,且客户地址在深圳片区的

    为了便于理解,我们在这里先定义两个实体类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    /// <summary>
    /// 订单主表
    /// </summary>
    public class OrderMain
    {
        public Guid Id { get; set; }
        public string Code { get; set; }
        public DateTime BillDate { get; set; }
        public Guid CustomerId { get; set; }
        public virtual Customer Customer { get; set; }
        public decimal TotalAmount { get; set; }
        public int Status { get; set; }
    }
    /// <summary>
    /// 客户信息
    /// </summary>
    public class Customer
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public string Area { get; set; }
    }

    那么我们这里使用的查询条件可能就会写成如下形式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var exp=EFExpression<OrderMain>.CreateBinaryExpression(o=>o.Status,EBinaryType.Equal,0);   //还没有送货
     
    exp&=EFExpression<OrderMain>.CreateBinaryExpression(o=>o.TotalAmount ,EBinaryType.LessThan,500000);  //小于50W
     
    var exp2=EFExpression<OrderMain>.CreateBinaryExpression(o=>o.Status,EBinaryType.Equal,1);   //送货中
     
    exp2&=EFExpression<OrderMain>.CreateBinaryExpression(o=>o.Customer.Area,EBinaryType.Equal,”0755”);  //地址在深圳片区
     
    exp|=exp2;
     
    var queryExpression=exp.GetExpression();

    测试通过.大笑,至于其他的子类在此就不一一列举了,当然我们可以通过扩展一些写法使之更容易使用,比如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    /// <summary>
           /// 在当前条件基础上,构建一个like条件,并且和当前条件产生And运算
           /// </summary>
           /// <param name="property"></param>
           /// <param name="val"></param>
           /// <returns></returns>
           public static EFExpression<T> AndLike<T>(this EFExpression<T> old, Expression<Func<T, string>> property, string val) where T : class
           {
               var temp = new LikeEFExpression<T>(property, val);
               if (old == null)
                   return temp;
               else
                   return new LogicEFExpression<T>(old, ELogicType.And, temp);
           }
     
           /// <summary>
           /// 在当前条件基础上,构建一个等于条件,并且和当前条件产生And运算
           /// </summary>
           /// <typeparam name="TVal"></typeparam>
           /// <param name="old"></param>
           /// <param name="property"></param>
           /// <param name="val"></param>
           /// <returns></returns>
           public static EFExpression<T> AndEqual<T, TVal>(this EFExpression<T> old, Expression<Func<T, TVal>> property, TVal val)
               where TVal : IComparable
               where T : class
           {
               var temp = new BinanryEFExpression<T, TVal>(property, EBinaryType.Equal, val);
               if (old == null)
                   return temp;
               else
                   return new LogicEFExpression<T>(old, ELogicType.And, temp);
           }
     
           /// <summary>
           /// 在当前条件基础上,构建一个大于条件,并且和当前条件产生And运算
           /// </summary>
           /// <typeparam name="TVal"></typeparam>
           /// <param name="old"></param>
           /// <param name="property"></param>
           /// <param name="val"></param>
           /// <returns></returns>
           public static EFExpression<T> AndGreaterThan<T, TVal>(this EFExpression<T> old, Expression<Func<T, TVal>> property, TVal val)
               where TVal : IComparable
               where T : class
           {
               var temp = new BinanryEFExpression<T, TVal>(property, EBinaryType.GreaterThan, val);
               if (old == null)
                   return temp;
               else
                   return new LogicEFExpression<T>(old, ELogicType.And, temp);
           }

    那么我们在使用的时候就可以方便的写成下面这种形式

    var exp=EFExpression<OrderMain>.Create().AndEqual(o=>o.Status,0).AndGreaterThan(o=>o.TotalAmount ,500000)…..

    在此不再赘述,有兴趣的朋友们可以自己去写出类似的东东,或问我要源码包...

    http://www.cnblogs.com/xie-zhonglai/archive/2012/04/10/2440569.html

  • 相关阅读:
    c# 泛型总结
    透过字节码分析java基本类型数组的内存分配方式。
    c#索引器
    redis在asp.net 中的应用
    Unity3D shaderLab
    Unity3d Asset Store 打不开
    C# 类型转换的开销
    [转]权重算法
    Coroutine的原理以及实现
    在Unity3D里使用WinForm
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2441782.html
Copyright © 2011-2022 走看看