回顾

  上一篇对模式进行了介绍,并基于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. windows下python检查文件是否被其它文件打开.md

    有时候我们需要能够判断一个文件是否正在被其它文件访问,几乎不可避免的要调用操作系统接口 from ctypes import cdll import os _sopen = cdll.msvcrt._ ...

  2. 安装完grunt和grunt-cli仍然无法识别grunt

    如题: 在安装完grunt-cli和grunt之后,仍然不识别grunt. 反复确认是-g安装... 原因: 有可能是nodejs安装出现问题,到时npm的路径没有出现在环境变量里面. 把C:\Use ...

  3. OpenCV Linux 安装 Make出错

    执行完CMake之后再执行make时遇到以下错误 [ %] Generating precomp.hpp.gch/opencv_core_Release.gch In file included /b ...

  4. 详解Bootstrap缩略图组件及警示框组件

    缩略图组件 缩略图在网站中最常用的就是产品列表页面,一行显示几张图片,有的在图片底下带有标题.描述内容.按钮等信息.bootstrap框架将这部分独立成一个模块组件,通过类名.thumbnail配合b ...

  5. php三种基础算法:冒泡,插入和快速排序法

    $array = array(2,3,5,6,9,8,1); //冒泡排序思想,前后元素比较 function sort_bulldle($array){ $num = count($array); ...

  6. Android图片处理-相机、相处简单调用

    安卓开发中,常常需要使用到手机相机拍照.或者相册上传头像等等.通过使用Intent,我们很方便地获得相机.相册里面的图片: 1.相机调用,通过设置File文件路径和文件名,可以将拍照得到的图片保存下来 ...

  7. C#通过SQL 添加,删除,或者修改表名。

    这是我在 https://forums.asp.net/t/2106051.aspx?Create+Dynamic+table+in+SQL+using+C+ 的回复,如果其他人需要,可以参考 如果你 ...

  8. iOS设备类型

    通常App都会采集用户的设备信息,比如设备类型.网络类型.内存大小等,而拿到的数据比如:iPhone 8,1是什么意思?代表iOS 8.1吗,非也.这里放二个网站大家可以上去查一查,在统计分析的系统里 ...

  9. cmd for 循环拷贝文件

    这几天忙活部署测试环境, 中途需要拷贝 文件, 直接贴code吧: ::/定义原路径 set source=seventrat_test_backend,seventrat_test_frontend ...

  10. 曲率已驱动了头发——深度分析谷歌AlphaGo击败职业棋手

    这篇是我们自开设星际随笔以来写得最长的一篇.我们也花了不少力气.包括把那5盘棋各打了两遍的谱,包括从Nature官网上把那篇谷歌的报告花了200元下载下来研究它的算法(后来发现谷 歌网站上可以免费下载 ...