Query Object--查询对象模式(下)
回顾
上一篇对模式进行了介绍,并基于ADO.NET进行了实现,虽然现在ORM框架越来越流行,但是很多中小型的公司仍然是使用ADO.NET来进行数据库操作的,随着项目的需求不断增加,业务不断变化,ADO.NET的实现方式,会使原先简单的单表操作变得尤为复杂,特别是数据库表发生改变的情况下,无法像ORM框架那样,通过修改映射来达到统一的修改,需要靠程序员检查每一段相关的SQL来排查错误,这是非常麻烦的。
不管什么样的框架,使用起来不简单不易用的话,那么就没有设计的必要了。
因此今次的文章将会基于ORM框架来进行实现,大致内容如下:
- 基于表达式的实现
- 使用NHibernate查询
- 使用BeeGo查询
基于表达式的实现
因此在如今在C#领域内,如果没有Linq风格,对于编码人员来说就显得有些复杂了,因此扩展方向上肯定是要支持Linq。上一篇文章并没有实现对Linq的支持,而是留给大家去实现了,因此文章开头,就先复习一下吧。
首先从简单的调用方式开始吧,如:
query.Add<School>(s => s.Name == "一中");
或者
query.Add<School>(s => s.Age > 20);
分析以上两个查询条件表达式,并且跟原先的Criterion来进行对比,依然是可以通过解析表达式来生成Criterion对象的,但是由于NHibernate已经支持表达式了,因此需要重构一下Query Object模式,首先删除Criterion类,并对Query代码进行修改,代码如下:
private List<Expression> m_Criterions = new List<Expression>();
public IEnumerable<Expression> Criterions { get { return m_Expressions; } } public void Add<T>(Expression<Func<T, bool>> exp)
{
Add(exp as Expression);
} public void Add(Expression exp)
{
if (exp.NodeType == ExpressionType.Lambda)
Add((exp as LambdaExpression).Body);
else
m_Criterions.Add(exp);
}
接下来,需要支持and或者or了,由于and和or涉及到子查询的问题,当父查询的QueryOperator与子查询的QueryOperator不同的情况下,表达式就需要被转换成一个子查询了,因此代码改为:
public void Add(Expression exp)
{
if (exp.NodeType == ExpressionType.Lambda)
Add((exp as LambdaExpression).Body);
else if (exp.NodeType == ExpressionType.OrElse || exp.NodeType == ExpressionType.AndAlso)
AddByJunctionExpression(exp);
else
m_Expressions.Add(exp);
} private void AddByJunctionExpression(Expression exp)
{
var binaryExp = exp as BinaryExpression;
if ((Operator == QueryOperator.And && exp.NodeType == ExpressionType.AndAlso) ||
(Operator == QueryOperator.Or && exp.NodeType == ExpressionType.OrElse))
{
Add(binaryExp.Left);
Add(binaryExp.Right);
}
else
{
Query subQuery = new Query(exp.NodeType == ExpressionType.OrElse ? QueryOperator.Or : QueryOperator.And);
subQuery.Add(binaryExp.Left);
subQuery.Add(binaryExp.Right);
AddSubQuery(subQuery);
}
}
到这里基于表达式的Query Object就改造完成了,那么接下来就要根据数据层具体的环境来讲Query转化为对应的API来查询数据了。
使用NHibernate查询
先上代码,然后分析,大致代码为:
public static NHibernate.ICriterion ToNHibernateQuery<T>(Query query)
{
NHibernate.Junction junction;
if (query.Operator == QueryOperator.And)
junction = NHibernate.Expression.Conjunction();
else
junction = NHibernate.Expression.Disjunction(); if (expressions.Any())
{
foreach (var exp in expressions)
AppendNHibernateCriterionByExpression(junction, exp);
} this.AppendNHibernateCriterionBySubQueries(junction);
return junction;
} private static void AppendNHibernateCriterionByExpression<T>(NHibernate.Junction junction, Expression exp)
{
var binaryExp = exp as BinaryExpression;
var expression = Expression.Lambda<Func<T, bool>>(
exp,
GetParameterExpressionBy(binaryExp.Left) ?? GetParameterExpressionBy(binaryExp.Right));
junction.Add(expression);
} private static ParameterExpression GetParameterExpressionBy(Expression exp)
{
if (exp.NodeType != ExpressionType.MemberAccess)
return null; var memberExp = exp as MemberExpression;
return memberExp.Expression as ParameterExpression;
} private static void AppendNHibernateCriterionBySubQueries<T>(NHibernate.Junction junction, IEnumerable<Query> subQueries)
{
if (!subQueries.Any())
return; foreach (var subQuery in subQueries)
{
var subCriterion = ToNHibernateQuery<T>(subQuery);
junction.Add(subCriterion);
}
}
由于NHibernate内部已经实现了Query Object模式,因此在转换的过程当中,只需要将And和OR条件转化为对应的NHibernate类就行了,然后利用NHibernate对于表达式的支持将条件添加进去,最后使用ICriteria.List<T>()获取结果就可以了。
上一篇文章有提到对于Query Object模式对于外联的支持是比较麻烦的,但是在NHibernate的基础下去实现是比较简单的,这里就不再做过多的介绍了,有意向的朋友要对NHbernate做深入的研究,这里推荐大家看看李永京的文章学习一下,或者买基本NHibernate的书学习。
使用BeeGo查询
GO语言出来也有一段时间了,很多大公司也都在使用它,我们也不能落后。由于最近使用BeeGo框架搭建了数据服务器,Query Object模式当然也是必须的,因此借着这篇文章,也顺带讲讲BeeGo框架上,Query Object模式的实现。
虽然现在大部分的公司使用的数据库依然是关系型数据库,但是仍然挡不住NoSQL这个新兴数据库的脚步,由于最近一段时间都是是用Nodejs来进行web开发的,因此使用NOSQL数据库能更好的进行数据库交互,毕竟可以直接使用JSON数据结构,但是由于同事对于关系型数据库比较习惯因此不得不改为关系型数据库,于是就有了上面提到的GO数据服务器了。
为了使原先查询数据的方式不用改变,因此引用了MongoDb的API结构来实现Query Object模式,ajax查询代码为:
$.ajax({
url: '/school/find',
data: {
$or: {
name: '一中',
age: {
$gt: 20
}
}
}
});
以上结构等同于select * from school where name = '一中' or age > 20,按照以前几次实现Query Object的经验,这次要实现这种结构的转换相对还是比较简单的,在Go语言当中,通用类型是interface{},它相当于C#的Object,字典也有一些差别,但是并不妨碍具体的实现,这里为了简便(使用Go的结构太复杂了),因此直接使用switch来转换成begoo的接口的,大致代码如下:
func (this *Repository) translateToCriterion(field string, criterion map[string]interface{}, isAnd bool, isNot bool) *orm.Condition {
cond := orm.NewCondition()
var value interface{}
for k, v := range criterion {
key := field
value = v
switch k {
case "$take":
continue
case "$skip":
continue
case "$sort":
continue
case "$like":
startsWith, endsWith := strings.HasSuffix(v.(string), "%"), strings.HasPrefix(v.(string), "%")
chars := []byte(v.(string))
if startsWith && endsWith {
key = key + "__icontains";
value = string(chars[1:len(chars)-1])
} else if startsWith {
key = key + "__istartswith"
value = string(chars[:len(chars)-1])
} else {
key = key + "__iendswith"
value = string(chars[1:])
}
break
case "$gt":
key = key + "__gt"
break
case "$lt":
key = key + "__lt"
break
case "$in":
key = key + "__in"
break
case "$not":
if reflect.TypeOf(v).Kind() == reflect.Map {
value = this.translateToCriterion(field, v.(map[string]interface{}), isAnd, true)
} else {
isNot = true
}
break
case "$and":
value = this.translateToCriterion("", v.(map[string]interface{}), true, isNot)
break
case "$or":
value = this.translateToCriterion("", v.(map[string]interface{}), false, isNot)
break
default:
if v != nil && reflect.TypeOf(v).Kind() == reflect.Map {
value = this.translateToCriterion(k, v.(map[string]interface{}), isAnd, isNot)
} else if v == nil {
key = k + "__isnull"
value = true
} else {
key = k
}
break
}
subCond, isCond := value.(*orm.Condition)
if isAnd {
if isCond {
cond = cond.AndCond(subCond)
} else if isNot {
cond = cond.AndNot(key, value)
} else {
cond = cond.And(key, value)
}
} else {
if isCond {
cond = cond.OrCond(subCond)
} else if isNot {
cond = cond.OrNot(key, value)
} else {
cond = cond.Or(key, value)
}
}
}
return cond
}
这样就实现了BeeGo框架下的Query Object模式了,如果对GO有兴趣的话,也可以直接使用BeeGo框架来搭建Web应用,那么前端可以直接使用前面那样的ajax来实现数据的访问,这样还是很方便的。
结尾
Query Obejct模式虽然可以简化数据的查询,虽然对于数据权限是没有作用的,但是由于使用Query Object模式,查询接口是固定的,因此可以在查询方法内添加数据权限模块,这样可以简化数据权限实现的困难。
那么这次的文章就到这里了,如有疑问和错误请大家留言给我,谢谢。
Query Object--查询对象模式(下)的更多相关文章
- Query Object--查询对象模式(上)
回顾 上两篇文章主要讲解了我对于数据层的Unit Of Work(工作单元模式)的理解,其中包括了CUD的操作,那么今天就来谈谈R吧,文章包括以下几点: 什么是Query Object 基于SQL的实 ...
- 【 PostgreSQL】查询某模式下所有表的分布键信息
想看下某模式下所有表创建的分布键是否合理,查找系统表文档拼出如下sql,亲们如果有更好的sql或者意见欢迎留言! SELECT aaa.nspname AS "模式名", ...
- 【gp数据库】查询系统表看模式下所有表的分布键信息
Greenplum是关系型的分布式数据库,需要存储的数据库在进入数据库时,将先进行数据分布的处理工作,讲一个表的数据平均分不到每个节点上,并为每个表指定一个分发列(distribute Column) ...
- 被遗忘的设计模式——空对象模式(Null Object Pattern)
GoF(四人帮)那本<设计模式 可复用面向对象软件的基础>可谓是设计模式方面的经典之作,其中介绍的23种设计模式, 也可谓是经典中的经典.但是,设计模式的种类绝不仅仅是这23种,除此之外还 ...
- ElementUI Tree控件在懒加载模式下的重新加载和模糊查询
之所以使用懒加载是为了提高性能,而且只有在懒加载模式下默认会给所有显示节点设置展开按钮.leaf也可以做到,但是要操作数据比较麻烦. 要实现懒加载模式下的模糊查询以及重新加载必须要使用data与laz ...
- “Options模式”下的配置是如何绑定为Options对象
“Options模式”下的配置是如何绑定为Options对象 配置的原子结构就是单纯的键值对,并且键和值都是字符串,但是在真正的项目开发中我们一般不会单纯地以键值对的形式来使用配置.值得推荐的做法就是 ...
- 设计模式:空对象模式(Null Object Pattern)
设计模式:空对象模式(Null Object Pattern) 背景 群里聊到<ASP.NET设计模式>,这本书里有一个“Null Object Pattern”,大家就闲聊了一下这个模式 ...
- 空对象模式(Null Object Pattern)
空对象模式:用一个空对象来取代null实例的检查,空对象实现一个不做任何动作的关系.(消除如if(Object == null) 这样的检查null实例代码) 例子: public abstract ...
- JS函数的参数对象arguments在严格模式下的限制
在JS中,传入的函数的参数个数可以与定义函数的个数不一致,那么对于传入的实参的引用,则是arguments对象.然而改对象在严格模式和非严格模式下是由区分的: 1 在严格模式下arguments作为了 ...
随机推荐
- Lingo 做线性规划 - Game Thoery
Reference: <An Introduction to Management Science Quantitative Approaches to Decision Making, Rev ...
- jquery判断页面滚动条(scroll)是上滚还是下滚,且是否滚动到头部或者底部
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- jQuery实现左移右移
<html> <head> <meta charset="utf-8"> <title>完成左移右移</title> & ...
- MyEclipse使用总结——MyEclipse去除网上复制下来的来代码带有的行号
一.正则表达式去除代码行号 作为开发人员,我们经常从网上复制一些代码,有些时候复制的代码前面是带有行号,如: MyEclipse本身自带有查找替换功能,并且支持正则表达式替换,使用正则替换就可以很容易 ...
- C#Winform程序如何发布并自动升级(图解)
C#Winform程序如何发布并自动升级(图解) 有不少朋友问到C#Winform程序怎么样配置升级,怎么样打包,怎么样发布的,在这里我解释一下打包和发布 关于打包的大家可以看我的文章C# w ...
- The main difference between Java & C++(转载)
转载自:http://stackoverflow.com/questions/9192309/the-main-difference-between-java-c C++ supports point ...
- ubuntu 16.04 vnc server
安装方法 https://help.ubuntu.com/community/VNC/Servers#vino 系统默认自带的,通过搜索Remote Desktop来找到它 配置好之后,设定一下密码 ...
- Ejabberd V.S Openfire
ejabberd Openfire homepage https://www.ejabberd.im/ http://www.igniterealtime.org/projects/openfire/ ...
- Openresty 安装教程
Openresty的简单安装方法,如需高级编译安装,请参照安装选项 1.安装配置好Yum源,不赘述此步骤 2.安装必要组件 yum install pcre-devel openssl-devel g ...
- Sql server Always On 读写分离配置方法
使用了Sqlserver 2012 Always on技术后,假如采用的配置是默认配置,会出现Primary server CPU很高的情况发生,比如默认配置如下: 需要自定义来解决这个问题. 我们先 ...