使用EF构建企业级应用(三)
2012-04-10 14:42 谢中涞 阅读(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)),
- 设定:var exp1=a>4 and b<3; var exp2=d>5 and c in(3,4,5),则上述条件可以表示为 var exp=exp1 or exp2;
- 进一步分解exp1:var exp1_1=a>4; var exp1_2=b<3; 则exp1=exp1_1 and var exp_2
- 进一步分解exp2:var exp2_1=d>5; var exp2_2=c in (3,4,5); 则exp2=exp2_1 and exp2_2
有了上面的分解,我们发现其实构建这个查询条件非常符合我们常见的递归算法.下面我们尝试一下用递归的思想来实现这个.我们先大致的画个UML草图
为了不和系统下面的Expression 命名混淆,我们这里采用EFExpression作为条件表达式基类名称,
- EFExpression<T> 为一个抽象基类,里面主要有一个返回类型为 Expression<Func<T,bool>> GetExpression()的一个抽象方法,其具体实现,我们放在了每一个具体的Expression中去定义.
- EmptyEFExpression<T> 表示一个空的查询表达式
- BinaryEFExpression<T> 表示一个基于二元条件的查询表达式,如大于,小于,等于 ……
- LikeEFExpression<T> 表示一个用于创建”类似于”条件的查询表达式
- LogicEFExpression<T> 表示一个逻辑运算的查询表达式,如and or
- ….可能还会有其他子类
1.首先我们试着来编写一下抽象基类EFExpression<T>的实现
[+]查看代码
2. 接着我们来看一下如何实现这个BinaryEFExpression<T>
[+]查看代码
3.我们在来看一下关于表达式逻辑运算的LogicEFExpression<T> 类又是如何实现的
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980/// <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.为了使用方便,我们在基类中再添加个静态方法
12345678910111213/// <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.有了上面的实现,我们来测试下我们的想法.为了让我们更容易理解,我们假设在一个订单管理环境中,有如下一个需求:请查询满足以下任意一个条件中的订单记录
- 订单状态还未送货且金额>50W的
- 订单状态为送货中,且客户地址在深圳片区的
为了便于理解,我们在这里先定义两个实体类
12345678910111213141516171819202122/// <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(); |
测试通过.,至于其他的子类在此就不一一列举了,当然我们可以通过扩展一些写法使之更容易使用,比如
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152/// <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)…..
在此不再赘述,有兴趣的朋友们可以自己去写出类似的东东,或问我要源码包...
本着互相分享的精神,文章欢迎转载,但转载需要标明本文出处.除特殊声明外,本文章均首发于博客园.
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步