• 什么是Query Object模式

  • Query Object的架构设计

  • Query Object在服务层的应用

  • 测试

Query Object模式

Query Object:可以在领域服务层构造查询然后传给资源库使用,并使用某种查询翻译器将对象查询(Query)翻译成底层数据库持久化框架可以理解的查询(即翻译成一条Sql 语句)。而Query Object即可以理解为表示数据库查询的对象。且可以构造任意查询,然后传给Repository。Query Object模式的主要好处是它完全将底层的数据库查询语言抽象出来。

如果没有某种查询机制,我们的持久化层可能会这样定义方法:

    public interface IOrderRepository{IEnumerable<Order> FindAll(Query query);IEnumerable<Order> FindAllVipCustomer();IEnumerable<Order> FindOrderBy(Guid customerId);IEnumerable<Order> FindAllCustomersWithOutOrderId();}

很明显,可以看出持久化层很不简洁,Repository将充满大量检索方法,而我们希望我们的持久化层尽量简洁些,根据传入参数能够动态的翻译成数据库查询语言,就像下面写的这样:

public interface IOrderRepository{       IEnumerable<Order> FindBy(Query query);IEnumerable<Order> FindBy(Query query, int index, int count);         }

这个Query就是核心——一个表示数据库查询的对象,好处是显而易见的:完全将底层的数据库查询语言抽象出来,因此将数据持久化和检索的基础设施关注点从业务层中分离出来。

Query Object模式的架构

  • 添加一个枚举,CriteriaOperator:
public enum CriteriaOperator{Equal,//=LessThanOrEqual,// <=NotApplicable//≠// TODO: 省略了其他的操作符,可继续添加}

  • 接着添加Criterion类,表示构成查询的过滤器部分:指定一个实体属性(OR  Mapping)、要比较的值以及比较方式:
 public class Criterion{private string _propertyName;//实体属性private object _value;//进行比较的值private CriteriaOperator _criteriaOperator;//何种比较方式public Criterion(string propertyName, object value, CriteriaOperator criteriaOperator){_propertyName = propertyName;_value = value;_criteriaOperator = criteriaOperator;}public string PropertyName {get { return _propertyName; }}public object Value{get { return _value; }}public CriteriaOperator criteriaOperator{get { return _criteriaOperator; }}/// <summary>/// Lambda表达式树:创建一个过滤器/// </summary>/// <typeparam name="T"></typeparam>/// <param name="expression"></param>/// <param name="value"></param>/// <param name="criteriaOperator"></param>/// <returns></returns>public static Criterion Create<T>(Expression<Func<T, object>> expression, Object value, CriteriaOperator criteriaOperator){string propertyName = PropertyNameHelper.ResolvePropertyName<T>(expression);Criterion myCriterion = new Criterion(propertyName, value, criteriaOperator);return myCriterion;}}

  • 为了避免在构建查询时出现令人畏惧的魔幻字符串,我们创建一个辅助方法,使用表达式参数。
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);}}

这样就可以像查询中添加一个新的查询条件:

query.Add(Criterion.Create<Order>(c=>c.CustomerId,customerId,CriteriaOperator.Equal));

而不是使用魔幻字符串:

  query.Add(new Criterion("CustomerId", customerId, CriteriaOperator.Equal));

  • 下面要创建表示查询的排序属性:
 public class OrderByClause{public string PropertyName { get; set; }public bool Desc { get; set; }}

  • 接着,创建另一个枚举,确定如何各个Criterion进行评估:
public enum QueryOperator{And,Or            }

  • 有时候的复杂非常难以创建,在这些情况下,可以使用指向数据库视图或存储过程的命名查询,添加一个QueryName来存放查询列表:
 public enum QueryName{       Dynamic = 0,//动态创建RetrieveOrdersUsingAComplexQuery = 1//使用已经创建好了的存储过程、视图、特别是查询比较复杂时使用存储过程}

  • 最后,添加Query类,将Query Object模式组合在一起:
    public class Query{private QueryName _name;private IList<Criterion> _criteria;public Query(): this(QueryName.Dynamic, new List<Criterion>()){ }public Query(QueryName name, IList<Criterion> criteria){ _name = name;_criteria = criteria;}public QueryName Name{get { return _name; }}/// <summary>/// 判断该查询是否已经动态生成或与Repository中某个预先建立的查询相关/// </summary>/// <returns></returns>public bool IsNamedQuery(){return Name != QueryName.Dynamic;}public IEnumerable<Criterion> Criteria{get {return _criteria ;}}          public void Add(Criterion criterion){if (!IsNamedQuery())// 动态查询
                _criteria.Add(criterion);elsethrow new ApplicationException("You cannot add additional criteria to named queries");}public QueryOperator QueryOperator { get; set; }public OrderByClause OrderByProperty { get; set; }}

  • 最后创建一个工厂类,提供已存在的查询:
 public static class NamedQueryFactory{public static Query CreateRetrieveOrdersUsingAComplexQuery(Guid CustomerId){IList<Criterion> criteria = new List<Criterion>();Query query = new Query(QueryName.RetrieveOrdersUsingAComplexQuery, criteria);criteria.Add(new Criterion ("CustomerId", CustomerId, CriteriaOperator.NotApplicable));return query;}}

Query Object在服务层的运用

  • 建立领域模型和领域服务类:
 public class Order{public Guid Id { get; set; }public bool HasShipped { get; set; }public DateTime OrderDate { get; set; }public Guid CustomerId { get; set; }}

  • 添加Repository接口:
  public interface IOrderRepository{       IEnumerable<Order> FindBy(Query query);IEnumerable<Order> FindBy(Query query, int index, int count);         }

  • 建立领域服务层:
    public class OrderService{private IOrderRepository _orderRepository;public OrderService(IOrderRepository orderRepository){_orderRepository = orderRepository;}public IEnumerable<Order> FindAllCustomersOrdersBy(Guid customerId){IEnumerable<Order> customerOrders = new List<Order>();Query query = new Query();//推介使用这种query.Add(Criterion.Create<Order>(c=>c.CustomerId,customerId,CriteriaOperator.Equal));//输入魔幻字符串,容易出错query.Add(new Criterion("CustomerId", customerId, CriteriaOperator.Equal));query.OrderByProperty = new OrderByClause { PropertyName = "CustomerId", Desc = true };customerOrders = _orderRepository.FindBy(query); return customerOrders;}public IEnumerable<Order> FindAllCustomersOrdersWithInOrderDateBy(Guid customerId, DateTime orderDate){IEnumerable<Order> customerOrders = new List<Order>();Query query = new Query();query.Add(new Criterion("CustomerId", customerId, CriteriaOperator.Equal));query.QueryOperator = QueryOperator.And; query.Add(new Criterion("OrderDate", orderDate, CriteriaOperator.LessThanOrEqual));query.OrderByProperty = new OrderByClause { PropertyName = "OrderDate", Desc = true };customerOrders = _orderRepository.FindBy(query);return customerOrders;}public IEnumerable<Order> FindAllCustomersOrdersUsingAComplexQueryWith(Guid customerId){IEnumerable<Order> customerOrders = new List<Order>();Query query = NamedQueryFactory.CreateRetrieveOrdersUsingAComplexQuery(customerId);customerOrders = _orderRepository.FindBy(query);return customerOrders;}}

OrderService类包含3个方法,他们将创建的查询传递给Repository。FindAllCustomersOrdersBy和FindAllCustomersOrdersWithInOrderDateBy方法通过Criterion和OrderByClaus添加来创建动态查询。FindAllCustomersOrdersUsingAComplexQueryWith是命名查询,使用NamedQueryFactory来创建要传给Repository的Query Object。

  • 最后创建一个翻译器:QueryTranslator,将查询对象翻译成一条可在数据库上运行的Sql命令:
public static class OrderQueryTranslator{private static string baseSelectQuery = "SELECT * FROM Orders ";public static void TranslateInto(this Query query, SqlCommand command){if (query.IsNamedQuery()){command.CommandType = CommandType.StoredProcedure;command.CommandText = query.Name.ToString();foreach (Criterion criterion in query.Criteria){command.Parameters.Add(new SqlParameter("@" + criterion.PropertyName, criterion.Value));}}else{StringBuilder sqlQuery = new StringBuilder();sqlQuery.Append(baseSelectQuery);bool _isNotfirstFilterClause = false;if (query.Criteria.Count() > 0)sqlQuery.Append("WHERE ");   foreach (Criterion criterion in query.Criteria){if (_isNotfirstFilterClause)sqlQuery.Append(GetQueryOperator(query));                                            sqlQuery.Append(AddFilterClauseFrom(criterion));command.Parameters.Add(new SqlParameter("@" + criterion.PropertyName, criterion.Value));_isNotfirstFilterClause = true;}sqlQuery.Append(GenerateOrderByClauseFrom(query.OrderByProperty));command.CommandType = CommandType.Text; command.CommandText = sqlQuery.ToString();}}private static string GenerateOrderByClauseFrom(OrderByClause orderByClause){return String.Format("ORDER BY {0} {1}",FindTableColumnFor(orderByClause.PropertyName), orderByClause.Desc ? "DESC" : "ASC");          }private static string GetQueryOperator(Query query){if (query.QueryOperator == QueryOperator.And)return "AND ";elsereturn "OR ";}private static string AddFilterClauseFrom(Criterion criterion){return string.Format("{0} {1} @{2} ", FindTableColumnFor(criterion.PropertyName), FindSQLOperatorFor(criterion.criteriaOperator), criterion.PropertyName);}private static string FindSQLOperatorFor(CriteriaOperator criteriaOperator){switch (criteriaOperator){ case CriteriaOperator.Equal:return "=";case CriteriaOperator.LessThanOrEqual:return "<=";default:throw new ApplicationException("No operator defined.");}}private static string FindTableColumnFor(string propertyName){switch (propertyName){case "CustomerId":return "CustomerId";case "OrderDate":return "OrderDate";default:throw new ApplicationException("No column defined for this property.");}}}

  • 建立简单仓储对象:
 public class OrderRepository : IOrderRepository {        private string _connectionString;public OrderRepository(string connectionString){_connectionString = connectionString;}public IEnumerable<Order> FindBy(Query query){// Move to method below with Index and count
IList<Order> orders = new List<Order>();using (SqlConnection connection =new SqlConnection(_connectionString)){SqlCommand command = connection.CreateCommand();query.TranslateInto(command);              connection.Open();using (SqlDataReader reader = command.ExecuteReader()){while (reader.Read()){orders.Add(new Order{CustomerId = new Guid(reader["CustomerId"].ToString()),OrderDate = DateTime.Parse(reader["OrderDate"].ToString()),Id = new Guid(reader["Id"].ToString())                            });}}                }return orders;}public IEnumerable<Order> FindBy(Query query, int index, int count){throw new NotImplementedException();            }       }

测试

 [TestFixture]public class SQLQueryTranslatorTests{[Test]public void The_Translator_Should_Produce_Valid_SQL_From_A_Query_Object(){int customerId = 9;string expectedSQL = "SELECT * FROM Orders WHERE CustomerId = @CustomerId ORDER BY CustomerId DESC";Query query = new Query();query.Add(new Criterion("CustomerId", customerId, CriteriaOperator.Equal));//query.Add(Criterion.Create<Order>(c => c.CustomerId, customerId, CriteriaOperator.Equal));query.OrderByProperty = new OrderByClause { PropertyName = "CustomerId", Desc = true };SqlCommand command = new SqlCommand();query.TranslateInto(command);Assert.AreEqual(expectedSQL, command.CommandText);}}

转载于:https://www.cnblogs.com/OceanEyes/archive/2012/11/14/think-in-design-pattern-query-object.html

Thinking In Design Pattern——Query Object模式相关推荐

  1. 使用Query Object 模式 基于jpql实例

    为什么80%的码农都做不了架构师?>>>    Query Object 编程语言可以包含sql语句,但许多开发者对此并不太熟悉.而且,你需要了解数据库设计方案以便形成查询.可以通过 ...

  2. [Design Pattern] 抽象工厂模式

    抽象工厂模式 抽象工厂针对产品族,不针对产品等级结构 产品族:同一产地同一个厂商,功能不同. 产品等级结构:功能相同,但是产地和厂商不同. class AbstractApple { public:v ...

  3. Thinking In Design Pattern——Unit Of Work(工作单元)模式探索

    阅读目录 什么是Unit Of Work模式 建立Infrastructure 建立Model 建立Repository来持久化业务实体 回到顶部 什么是Unit Of Work模式 Unit Of ...

  4. Head First Design Pattern 读书笔记(4) 工厂模式

    2019独角兽企业重金招聘Python工程师标准>>> Head First Design Pattern 读书笔记(4) Factory Pattern 工厂模式 ##Factor ...

  5. Design Pattern Quick Overview

    Do we really need this design pattern? Just ignore, all the big guys requires it. For you, just unde ...

  6. 初读设计模式-----《design pattern explained》读后感

    从网上淘来了一本<design pattern explained>,用了半个月的时间细细的读完了这本书. 本打算单单的从技术角度进行总结的,但是却全然没有头绪.说说自己的粗浅的感悟吧.. ...

  7. Design Pattern 设计模式【观察者】

    观察者设计模式中存在两种角色: 观察者和被观察者. 最经典的例子就是"猫叫了,老鼠跑了,惊醒了主人.",这个例子也是我以前去微软面试的时候碰到的一个面试题,当时就是让我设计一下这个 ...

  8. DP什么意思 design pattern 设计模式 面向对象 概念大全

    DP  design pattern 大话设计模式  中的DP 是设计模式的意思 设计模式的书 ,最经典最原始的就是 GOF 的<设计模式>了. 设计模式的书基本上大多是以这 20 多个模 ...

  9. java tea bag_设计模式系列教程—Template Method Pattern(模板方法模式)

    9 Template Method Pattern(模板方法模式) 前言:封装步骤的算法. Vander作为老板,凡是亲力亲为,他新开了家咖啡店,这是他招牌咖啡卡布奇诺的冲泡方法: 1.把水煮沸 2. ...

最新文章

  1. Open3d学习计划—高级篇 8(网格变形)
  2. android源代码居中字体,Android (布局优化) TextView实现drawable图标大小 位置与第一行文本居中...
  3. php event_base_new,php event拓展基本使用
  4. linux vi行尾总是显示颜色,【转载】Linux 下使用 vi 没有颜色的解决办法
  5. LLBLGen Pro v4.2_Patch+Keygen
  6. element table 无数据时显示图片替换“暂无该数据“
  7. 联想电源管理软件 v6.68.8官方版
  8. uboot distro_bootcmd 理解
  9. Java反射学习笔记
  10. 数据库生成日然周、自然月、自然日
  11. 联想Y400安装ubuntu16.04、cuda8.0、cudnn5.1、anaconda2.7、tensorflow1.2、keras、opencv
  12. Practical Pigment Mixing for Digital Painting文献简单翻译
  13. 【墨者学院】身份认证失效漏洞实战
  14. 如何解决Win11系统崩溃的问题?
  15. 演讲文档和视频《元宇宙与区块链IT基础设施》下载
  16. SpringCloud Alibaba之 Sentinel流量防卫兵
  17. 超详细的【自动化测试步骤】,没想到能省下这么多时间!!
  18. Ubuntu 17.04 连不上网
  19. 关于关闭(或禁止)windows 10/ windows 11系统更新解决方案
  20. vuedraggable的使用

热门文章

  1. mysql+distinct+max_MySQL中distinct与group by之间的性能进行比较
  2. mysql 回滚之后抛出异常_在PHP中 开始事务后,程序抛出异常 没有执行commit也没有执行rollback mysql事务会回滚吗?...
  3. 改进版的CBOW模型
  4. c语言 vc++6.0集成开发环境的使用,VC6.0集成开发环境的使用.ppt
  5. Java面典_【Java实用工具】——使用oshi获取主机信息
  6. 鸿蒙os能超越ios吗,鸿蒙OS对比iOS,华为再次“超越”,流畅度大幅领先苹果!...
  7. apache php日志配置,HTML_初学:apache与php基本配置,1、APACHE的日志主要分为“ - phpStudy...
  8. acer软件保护卡清除工具clear_如何清除 APT 缓存来回收宝贵的磁盘空间 | Linux 中国...
  9. python模块如何导入解释器_无法从嵌入式解释器导入内置模块(仅限Windows)
  10. 几张一模一样的照片_两张一模一样的照片看起来却不一样!什么鬼?