欢迎来到.net学习网

欢迎联系站长一起更新本网站!QQ:879621940

您当前所在位置:首页 » C# » 正文

Query Object模式设计示例

创建时间:2015年03月28日 11:14  阅读次数:(7390)
分享到:
Query Object模式设计
Query Object(查询对象设计),被设计为“一个可以生成数据库查询条件”的对象。如果没有这样的查询条件设计,我们的数据层可能会充斥着大量的查询方法(当然,接收where条件的万能查询设计我们就不讨论了),比如,我们可能会要求按产品分类来查询产品,也会被要求按产品名称查询产品,还有可能要求按产品编号来查询产品等等,这样我们在Repository中会存在3个或更多的查询方法,但如果我们设计Query Object对象,Repository中只需要存在一个接收Query Object对象的查询方法即可。

下面开始实现Query Object设计。

首先我们分析一下一要sql查询命令所需及可变的元素,看下面的sql:
select * from table1 where fields1=’xx001’  and fields2=’abc’ order by fields1 desc

1,table1是表名,换到我们的程序设计中就是model对象名,它是可变的。
2,fields1与fields2是表中的字段,换到程序中就是model对象的属性,它是可变的。
3,fields1与fields2中的and,是两个查询条件的连接符,字是可变的,但我们可以知道它变化的范围,无非就是 and 和 or,所以可以设计为枚举。
4,ields1=’xx001’中的=,是字段与值的比较符,我们可以知道它变化的范围,比如=、 >、<等,所以也可以设计为枚举。
5,order by fields1 desc中的desc,查询结果的排序方式,只有两个值 desc,asc,所以还是设计为枚举。

好了,下面开始真正代码:
先设计连接符枚举
/// <summary >
/// 多个查询条件的连接符
/// </summary >
public enum eCriteriaConnect
{
And,
Or
}

再就是字段与值的比较符
/// <summary >
/// 查询连接比较符
/// </summary >
public enum eCriteriaOperator
{
Equal,
Less,
Greater
}

这里我们只枚举了三个(等于,小于,大于),更多的比较请自己添加。

再下来就是查询结果的排序方式
public enum Sort
{
Desc,
Asc
}

有了上面三个枚举,我们开始创建查询条件类QueryFilter,代码如下:
public class QueryFilter
{
    private string propertyName;
    private object value;
    private eCriteriaOperator criteriaOperator;
    private eCriteriaConnect criteriaConnect;

    private QueryFilter(string propertyName, object value, eCriteriaOperator criteriaOperator)
    {
        this.propertyName = propertyName;
        this.value = value;
        this.criteriaOperator = criteriaOperator;
    }

    private QueryFilter(string propertyName, object value, eCriteriaOperator criteriaOperator, eCriteriaConnect criteriaConnect)
        : this(propertyName, value, criteriaOperator)
    {
        this.criteriaConnect = criteriaConnect;
    }

    public string PropertyName
    {
        get { return propertyName; }
    }

    public object Value
    {
        get { return value; }
    }

    public eCriteriaOperator CriteriaOperator
    {
        get { return criteriaOperator; }
    }

    public eCriteriaConnect CriteriaConnect
    {
        get { return criteriaConnect; }
}
}

解释下上面代码:
propertyName:要查询的字段,即where fields1=’xx001’中的fields1
value:字段的值,即where fields1=’xx001’中的value
criteriaOperator:字段与值的比较符,即where fields1=’xx001’中的=号
criteriaConnect:两个查询条件的连接符即ields1=’xx001’ and fields2=’abc’ 中的 and。
另外我们添加了两个构造函数,因为我们知道,如果查询中只有一个查询条件,是不需要criteriaConnect的。

结果排序类:
public class OrderByClause
{
    public string PropertyName { get; set; }
    public eSort SortType { get; set; }

    public OrderByClause(string propertyName, eSort sortType)
    {
        this.PropertyName = propertyName;
        this.SortType = sortType;
    }
}

这个类很简单
PropertyName:要排序的字段
SortType:排序的方式(升序还是降序)

查询类:
public class Query<T > where T:IModel
{
    private IList<QueryFilter > filterList;

    public IList<QueryFilter > FilterList
    {
        get
        {
            if (filterList == null)
            {
                filterList = new List<QueryFilter >();
            }

            return filterList;
        }
}

public OrderByClause order{get;set;}
}

这个类也很简单了,它是调用整个Query Object对象的入口,用来收集用户的所有条件,然后我们通过辅助类将其生成最终的sql语句,其中where T:IModel主要用来限制T必须是一个Model对象,这个不是必须的。

辅助类,将Query对象转换成sql并执行,返回一个DataTable
public static class QueryTranslator
{
    public static DataTable TranslatorSql<T >(Query<T > query)
    {
        StringBuilder strSql = new StringBuilder("select * from " + typeof(T).Name + " where ");
        strSql.Append(GetQueryFilterForStr(query));
        strSql.Append(" ");
        strSql.Append(GetOrder(query.order));

        SqlParameter[] parameters = InitParameter<T >(query);

        return DBUtility.DbHelperSQL.Query(strSql.ToString(), parameters);

    }

//将QueryFilter对象转为查询条件
    private static string GetQueryFilterForStr<T >(Query<T > query)
    {
        StringBuilder strSql = new StringBuilder(" where ");
        int filterListCount = query.FilterList.Count();
        bool isFirstFilter = true;
        for (int i = 0; i < filterListCount; i++)
        {
            if (!isFirstFilter)
                strSql.Append(" " + GetCriteriaConnect(query.FilterList[i].CriteriaConnect) + " ");

            strSql.Append(query.FilterList[i].PropertyName + GetCriteriaOperator(query.FilterList[i].CriteriaOperator) + "@" + query.FilterList[i].PropertyName);
            isFirstFilter = false;
        }

        return strSql.ToString();
    }

//生成SqlParameter参数集合
    private static SqlParameter[] InitParameter<T >(Query<T > query)
    {
        IEnumerable<Pair > filterList = query.FilterList.Select(
                s = > new Pair(s.PropertyName, s.Value));

        int parameterCount = filterList.Count();
        SqlParameter[] parameter = new SqlParameter[parameterCount];

        int i = 0;
        foreach (Pair t in filterList)
        {
            parameter[i] = new SqlParameter(t.First.ToString(), t.Second);
            i++;
        }

        return parameter;
    }

//生成排序条件
    private static string GetOrder(OrderByClause order)
    {
        StringBuilder orderStr = new StringBuilder();
        orderStr.Append(" order by");
        orderStr.Append(order.PropertyName);
        orderStr.Append(" ");
        orderStr.Append(order.SortType.ToString());

        return orderStr.ToString();
    }

//生成查询条件连接符
    private static string GetCriteriaConnect(eCriteriaConnect criteriaConnect)
    {
        switch (criteriaConnect)
        {
            default:
            case eCriteriaConnect.And:
                return "and";
            case eCriteriaConnect.Or:
                return "or";
        }
    }

//生成查询条件比较符
    private static string GetCriteriaOperator(eCriteriaOperator criteriaOperator)
    {
        switch (criteriaOperator)
        {
            default:
            case eCriteriaOperator.Equal:
                return "=";
            case eCriteriaOperator.Greater:
                return " >";
        }
    }
}

好了,到此该设计就完成了,我们可以像下面这样调用它
Query<Model.Product > query = new Query<Model.Product >();
QueryFilter filter =new  QueryFilter("ProductID","xxx_001", eCriteriaOperator.Equal);
query.FilterList.Add(filter);

filter =new  QueryFilter("ProductName","xxx_002", eCriteriaOperator.Equal,eCriteriaConnect.And);
query.FilterList.Add(filter);

OrderByClause order=new OrderByClause("ProduceID",eSort.Desc)
query.order=order;

DataTable dt = QueryTranslator.TranslatorSql(query);

在上面的调用中,我们出现了"ProduceID"类的硬编码字符,这样的字符在使用的时候很容易出错,我们要能够直接引用查询对象的属性就是最好了,我们可以通个增加一个辅助类来实现这个效果。
public static class PropertyNameHelper
{
    public static string ResolvePropertyName<T >(Expression<Func<T, object > > expression)
    {
        var expr = expression.Body as MemberExpression;
        if (expr == null)
        {
            var u = expression.Body as UnaryExpression;
            expr = u.Operand as MemberExpression;
        }

        return expr.ToString().substring(expr.ToString().IndexOf(".") + 1);
    }
}

上面辅助类可以帮助我们从p= >p.ProductID这样的lambda表达式中分析出属性的名称。这样我们可以将上面的代码
QueryFilter filter =new  QueryFilter("ProductID","xxx_001", eCriteriaOperator.Equal);

改写成这样子
QueryFilter filter =new  QueryFilter(PropertyNameHelper.ResolvePropertyName<Product >(p= >p.ProductID),"xxx_001", eCriteriaOperator.Equal);

这样是不是有进步了呢?

但这样还是比较麻烦,我们可以进一步改造QueryFilter类,在QueryFilter类中添加以下方法:
public static QueryFilter Create<T >(Expression<Func<T, object > > expression, object value, eCriteriaOperator criteriaOperator)
{
    string propertyName = PropertyNameHelper.ResolvePropertyName<T >(expression);

    QueryFilter queryFilter = new QueryFilter(propertyName, value, criteriaOperator);

    return queryFilter;
}

这样我们就可以将代码
QueryFilter filter =new  QueryFilter(PropertyNameHelper.ResolvePropertyName<Product >(p= >p.ProductID),"xxx_001", eCriteriaOperator.Equal);

改成这样子:
QueryFilter filter =QueryFilter .Create<Product >(p= >p.ProductID,"xxx_001", eCriteriaOperator.Equal);

这样就更加的方便了。

好了,大家也可以用这种方法改造OrderByClause类,让它也可以使用lamdba表达式。

最后的类图设计是这样的

图1


图2
来源:.net学习网
说明:所有来源为 .net学习网的文章均为原创,如有转载,请在转载处标注本页地址,谢谢!
【编辑:Wyf

打赏

取消

感谢您的支持,我会做的更好!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

最新评论

共有评论1条
  • #1楼  评论人:匿名  评论时间:2015-3-28 23:29:47
  • 学习了,多谢站长的分享!
发表评论:
留言人:
内  容:
请输入问题 71+96=? 的结果(结果是:167)
结  果: