Query Object--查询对象模式(下)

回顾html

  上一篇对模式进行了介绍,并基于ADO.NET进行了实现,虽然如今ORM框架愈来愈流行,可是不少中小型的公司仍然是使用ADO.NET来进行数据库操做的,随着项目的需求不断增长,业务不断变化,ADO.NET的实现方式,会使原先简单的单表操做变得尤其复杂,特别是数据库表发生改变的状况下,没法像ORM框架那样,经过修改映射来达到统一的修改,须要靠程序员检查每一段相关的SQL来排查错误,这是很是麻烦的。
前端

  无论什么样的框架,使用起来不简单不易用的话,那么就没有设计的必要了。程序员

  所以今次的文章将会基于ORM框架来进行实现,大体内容以下:web

  • 基于表达式的实现
  • 使用NHibernate查询
  • 使用BeeGo查询

基于表达式的实现ajax

  所以在现在在C#领域内,若是没有Linq风格,对于编码人员来讲就显得有些复杂了,所以扩展方向上确定是要支持Linq。上一篇文章并无实现对Linq的支持,而是留给你们去实现了,所以文章开头,就先复习一下吧。数据库

  首先从简单的调用方式开始吧,如:express

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模式,查询接口是固定的,所以能够在查询方法内添加数据权限模块,这样能够简化数据权限实现的困难。

  那么此次的文章就到这里了,若有疑问和错误请你们留言给我,谢谢。

相关文章
相关标签/搜索