代码改变世界

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

  谢中涞  阅读(3861)  评论(25编辑  收藏  举报

本系列目录:

使用EF构建企业级应用(一):主要讲数据库访问基类IRepository及Repository 的实现

使用EF构建企业级应用(二):主要讲动态排序扩展的实现

使用EF构建企业级应用(三):主要讲灵活的构建查询条件表达式Expression<Func<TEntity,bool>>.

使用EF构建企业级应用(四):主要讲下在MVC环境中前端开发中如何邮箱的使用,及一个实例源码包

在前两篇文章中,我们已经实现了基于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>的实现

[+]查看代码

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

[+]查看代码

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)…..

 

 

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

努力加载评论中...
点击右上角即可分享
微信分享提示