回顾

  上一篇对模式进行了介绍,并基于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--查询对象模式(下)的更多相关文章

  1. Query Object--查询对象模式(上)

    回顾 上两篇文章主要讲解了我对于数据层的Unit Of Work(工作单元模式)的理解,其中包括了CUD的操作,那么今天就来谈谈R吧,文章包括以下几点: 什么是Query Object 基于SQL的实 ...

  2. 【 PostgreSQL】查询某模式下所有表的分布键信息

    想看下某模式下所有表创建的分布键是否合理,查找系统表文档拼出如下sql,亲们如果有更好的sql或者意见欢迎留言! ​SELECT     aaa.nspname AS "模式名", ...

  3. 【gp数据库】查询系统表看模式下所有表的分布键信息

    Greenplum是关系型的分布式数据库,需要存储的数据库在进入数据库时,将先进行数据分布的处理工作,讲一个表的数据平均分不到每个节点上,并为每个表指定一个分发列(distribute Column) ...

  4. 被遗忘的设计模式——空对象模式(Null Object Pattern)

    GoF(四人帮)那本<设计模式 可复用面向对象软件的基础>可谓是设计模式方面的经典之作,其中介绍的23种设计模式, 也可谓是经典中的经典.但是,设计模式的种类绝不仅仅是这23种,除此之外还 ...

  5. ElementUI Tree控件在懒加载模式下的重新加载和模糊查询

    之所以使用懒加载是为了提高性能,而且只有在懒加载模式下默认会给所有显示节点设置展开按钮.leaf也可以做到,但是要操作数据比较麻烦. 要实现懒加载模式下的模糊查询以及重新加载必须要使用data与laz ...

  6. “Options模式”下的配置是如何绑定为Options对象

    “Options模式”下的配置是如何绑定为Options对象 配置的原子结构就是单纯的键值对,并且键和值都是字符串,但是在真正的项目开发中我们一般不会单纯地以键值对的形式来使用配置.值得推荐的做法就是 ...

  7. 设计模式:空对象模式(Null Object Pattern)

    设计模式:空对象模式(Null Object Pattern) 背景 群里聊到<ASP.NET设计模式>,这本书里有一个“Null Object Pattern”,大家就闲聊了一下这个模式 ...

  8. 空对象模式(Null Object Pattern)

    空对象模式:用一个空对象来取代null实例的检查,空对象实现一个不做任何动作的关系.(消除如if(Object == null) 这样的检查null实例代码) 例子: public abstract ...

  9. JS函数的参数对象arguments在严格模式下的限制

    在JS中,传入的函数的参数个数可以与定义函数的个数不一致,那么对于传入的实参的引用,则是arguments对象.然而改对象在严格模式和非严格模式下是由区分的: 1 在严格模式下arguments作为了 ...

随机推荐

  1. Seo的几个境界

    Seo的境界 第一层,弄些关键词排名上去. 是的,大部分人理解的Seoer,就到此为止 这里有技巧若干若干.很值得一些人去卖弄. 第二层,大量广泛的收录,很好的pr值 恭喜您,把握搜索长尾, 这种不显 ...

  2. Django MVC simple

  3. Atitit.列表页面and条件查询的实现最佳实践(2)------翻页 分页 控件的实现java .net php

    )------翻页 分页 控件的实现java .net php 1. 关于翻页有关的几大控件::搜索框控件,显示表格控件,翻页器,数据源控件.. 1 2. 翻页的显示格式:: 1 2.1. 通常ui- ...

  4. Dash

    作为一名死coder,每天最常见的动作就是查看各种API文档,你一定也有过同时打开N个窗口(HTML.PDF.CHM),不停的在编辑器与文档之间切换的感受吧?怎么说呢,其实我很讨厌这种枯燥无味的动作, ...

  5. PHP holiday1

    寒假觉得应该学点什么 ,既然决定了就去做吧 放假前就觉得php很好,那就来学一下 ----------------------------------------------------------- ...

  6. iOS开发中使用CocoaPods来管理第三方的依赖程序

    之前也碰到类似的问题,怎样去管理这些第三方的文件,虽然手动添加也不算麻烦. 写这篇文章主要参考了唐巧的博文,链接如下: http://blog.devtang.com/blog/2012/12/02/ ...

  7. android: 多线程编程基础

    9.1   服务是什么 服务(Service)是 Android 中实现程序后台运行的解决方案,它非常适合用于去执行那 些不需要和用户交互而且还要求长期运行的任务.服务的运行不依赖于任何用户界面,即使 ...

  8. Java反序列化漏洞执行命令回显实现及Exploit下载

    原文地址:http://www.freebuf.com/tools/88908.html 本文原创作者:rebeyond 文中提及的部分技术.工具可能带有一定攻击性,仅供安全学习和教学用途,禁止非法使 ...

  9. 使用Git Bash for Windows

    本篇体验Git Bash在Windows操作系统上的用法. 什么是Bash? 是一个Shell环境,Bourne Again Shell的缩写. 安装git for windows → http:// ...

  10. windows7文件共享

    工作组模式下: 参考:http://support.microsoft.com/kb/2533010/zh-cn 1.首先区分网络类型配置文件:家庭网络,工作网络还是公用网络 2.每种类型区分两种文件 ...