使用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)),
- 设定: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>的实现
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293/// <summary>/// 查询表达式基类/// </summary>publicabstractclassEFExpression<T> where T :class{/// <summary>/// 获取查询表达式/// </summary>/// <returns></returns>publicvirtualExpression<Func<T,bool>> GetExpression(){if(Expression !=null){var candidateExp = Expression.Parameter(typeof(T),"x");var exp = Expression.Lambda<Func<T,bool>>(Expression, candidateExp);returnexp;}else{returnnull;}}/// <summary>/// 查询条件对应表达式/// </summary>protectedExpression _Expression {get;set; }/// <summary>/// 参数表达式/// </summary>publicParameterExpression[] Parameters {get;set; }/// <summary>/// 获取对应的表达式/// </summary>internalabstractExpression Expression {get; }/// <summary>/// 获取表达式主题/// </summary>/// <typeparam name="T"></typeparam>/// <typeparam name="P"></typeparam>/// <param name="old"></param>/// <param name="property"></param>/// <returns></returns>protectedstaticExpression 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();returnproperty.Body;}ParameterExpressionVisitor visitor =newParameterExpressionVisitor(old.Parameters[0]);Expression memberExpr = visitor.ChangeParameter(property.Body);returnmemberExpr;}/// <summary>/// 获取一个空的表达式/// </summary>/// <returns></returns>publicExpression<Func<T,bool>> GetEmptyExpression(){return(Expression<Func<T,bool>>)(f =>true);}/// <summary>/// 两个条件进行And运算/// </summary>/// <param name="left"></param>/// <param name="right"></param>/// <returns></returns>publicstaticEFExpression<T>operator&(EFExpression<T> left, EFExpression<T> right){returnnewLogicEFExpression<T>(left, ELogicType.And, right);}/// <summary>/// 两个条件进行Or运行/// </summary>/// <param name="left"></param>/// <param name="right"></param>/// <returns></returns>publicstaticEFExpression<T>operator|(EFExpression<T> left, EFExpression<T> right){returnnewLogicEFExpression<T>(left, ELogicType.Or, right);}}
2. 接着我们来看一下如何实现这个BinaryEFExpression<T>
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586/// <summary>/// 二元运算查询条件/// </summary>/// <typeparam name="T">查询条件实体类型</typeparam>/// <typeparam name="TVal">需要比较的属性类型</typeparam>internalclassBinanryEFExpression<T, TVal> : EFExpression<T>where T :classwhere TVal : IComparable{/// <summary>/// 定义条件的实体属性/// </summary>privateExpression<Func<T, TVal>> property;/// <summary>/// 比较的值/// </summary>privateTVal val;/// <summary>/// 二元运算符/// </summary>privateEBinaryType binaryType;/// <summary>/// 实例化新的二元查询表达式/// </summary>/// <param name="property">定义条件的实体属性</param>/// <param name="binaryType">二元运算符</param>/// <param name="val">比较的值</param>publicBinanryEFExpression(Expression<Func<T, TVal>> property, EBinaryType binaryType, TVal val){if(property ==null)thrownewArgumentNullException("property");//if (val == null && binaryType != EBinaryType.Like)// throw new ArgumentNullException("val");this.property = property;this.val = val;this.binaryType = binaryType;}internaloverrideExpression 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){caseEBinaryType.Equal:tempExp = Expression.Equal(propertyBody, compareVal);break;caseEBinaryType.GreaterThan:tempExp = Expression.GreaterThan(propertyBody, compareVal);break;caseEBinaryType.GreaterThanOrEqual:tempExp = Expression.GreaterThanOrEqual(propertyBody, compareVal);break;caseEBinaryType.LessThan:tempExp = Expression.LessThan(propertyBody, compareVal);break;caseEBinaryType.LessThanOrEqual:tempExp = Expression.LessThanOrEqual(propertyBody, compareVal);break;caseEBinaryType.NotEqual:tempExp = Expression.NotEqual(propertyBody, compareVal);break;default:break;}_Expression = tempExp;}return_Expression;}}}
3.我们在来看一下关于表达式逻辑运算的LogicEFExpression<T> 类又是如何实现的
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980/// <summary>/// 带有逻辑运算的查询表达式/// </summary>/// <typeparam name="T"></typeparam>publicclassLogicEFExpression<T> : EFExpression<T> where T :class{privateEFExpression<T> left;privateEFExpression<T> right;privateELogicType logicType;/// <summary>/// 实例化新的逻辑运算查询表达式/// </summary>/// <param name="left"></param>/// <param name="logicType">逻辑运算类型</param>/// <param name="right"></param>publicLogicEFExpression(EFExpression<T> left, ELogicType logicType, EFExpression<T> right){if(left ==null|| right ==null)thrownewArgumentNullException("left 和 right 不能同时为空");this.left = left;this.right = right;this.logicType = logicType;}publicoverrideExpression<Func<T,bool>> GetExpression(){if(left ==null)returnright.GetExpression();elseif(right ==null)returnleft.GetExpression();else{//判断进行运算的两个条件是否为空if(leftisEmptyEFExpression<T> && rightisEmptyEFExpression<T>)returnleft.GetExpression();elseif(leftisEmptyEFExpression<T>)returnright.GetExpression();elseif(rightisEmptyEFExpression<T>)returnleft.GetExpression();var leftExp = left.GetExpression();var rightExp = right.GetExpression();Expression<Func<T,bool>> exp =null;if(leftExp ==null&& rightExp ==null)returnnewEmptyEFExpression<T>().GetExpression();else{if(leftExp ==null)returnrightExp;elseif(rightExp ==null)returnleftExp;else{switch(logicType){caseELogicType.And:exp = leftExp.And(rightExp);break;caseELogicType.Or:exp = leftExp.Or(rightExp);break;default:break;}}}returnexp;}}internaloverrideExpression Expression{get{returnnull;}}}
4.为了使用方便,我们在基类中再添加个静态方法
12345678910111213/// <summary>/// 构建一个二元查询表达式/// </summary>/// <typeparam name="TVal">比较值的类型</typeparam>/// <param name="property">定义条件的实体属性</param>/// <param name="binaryType">运算符类型</param>/// <param name="val">比较的值</param>/// <returns></returns>publicstaticEFExpression<T> CreateBinaryExpression<TVal>(Expression<Func<T, TVal>> property,EBinaryType binaryType, TVal val) where TVal : IComparable{returnnewBinanryEFExpression<T, TVal>(property, binaryType, val);}
5.有了上面的实现,我们来测试下我们的想法.为了让我们更容易理解,我们假设在一个订单管理环境中,有如下一个需求:请查询满足以下任意一个条件中的订单记录
- 订单状态还未送货且金额>50W的
- 订单状态为送货中,且客户地址在深圳片区的
为了便于理解,我们在这里先定义两个实体类
12345678910111213141516171819202122/// <summary>/// 订单主表/// </summary>publicclassOrderMain{publicGuid Id {get;set; }publicstringCode {get;set; }publicDateTime BillDate {get;set; }publicGuid CustomerId {get;set; }publicvirtualCustomer Customer {get;set; }publicdecimalTotalAmount {get;set; }publicintStatus {get;set; }}/// <summary>/// 客户信息/// </summary>publicclassCustomer{publicGuid Id {get;set; }publicstringName {get;set; }publicstringArea {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); //小于50Wvar 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>publicstaticEFExpression<T> AndLike<T>(thisEFExpression<T> old, Expression<Func<T,string>> property,stringval) where T :class{var temp =newLikeEFExpression<T>(property, val);if(old ==null)returntemp;elsereturnnewLogicEFExpression<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>publicstaticEFExpression<T> AndEqual<T, TVal>(thisEFExpression<T> old, Expression<Func<T, TVal>> property, TVal val)where TVal : IComparablewhere T :class{var temp =newBinanryEFExpression<T, TVal>(property, EBinaryType.Equal, val);if(old ==null)returntemp;elsereturnnewLogicEFExpression<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>publicstaticEFExpression<T> AndGreaterThan<T, TVal>(thisEFExpression<T> old, Expression<Func<T, TVal>> property, TVal val)where TVal : IComparablewhere T :class{var temp =newBinanryEFExpression<T, TVal>(property, EBinaryType.GreaterThan, val);if(old ==null)returntemp;elsereturnnewLogicEFExpression<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