Query Object
Posted vichin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Query Object相关的知识,希望对你有一定的参考价值。
如果不采用某种查询机制,则Repository将充斥着大量的检索方法。
public interface ICustomerRepository { IEnumerable<Customer> FindAll(); IEnumerable<Customer> FindAllVIPCustomers(); IEnumerable<Customer> FindByOrder(Guid ID); IEnumerable<Customer> FindAllCustomersThatHaveOutstandingOrders(); …… }
如果采用了Query Object模式,就能够构造出任意查询,然后传给Repository。
Query Object模式的主要好处是它完全的将底层的数据库查询语言抽象出来,因此将数据持久化和检索的基础设施的关注点从业务层中分离出来。但有时候,需要创建原始的数据库查询语言。这可以通过使用数据库特有的QueryTranslator(传入Query Object参数并将其转换成数据库语言)来实现。
namespace Chap7.QueryObject.Infrastructure { //在这个demo中,只定义如下的几个操作,如果需要更多的操作,还需要额外的定义它们 public enum CriteriaOperator { Equal, LessThanOrEqual, NotApplicable } }
namespace Chap7.QueryObject.Infrastructure { /// <summary> /// Criterion标识构成查询的过滤部分:指定一个实体属性,进行比较的值以及比较的方式。 /// </summary> public class Criterion { string _propertyName; object _value; CriteriaOperator _criteriaOperator; public Criterion(string propertyName, object value, CriteriaOperator criteriaOperator) { _propertyName = propertyName; _value = value; _criteriaOperator = criteriaOperator; } public string PorpertyName { get { return _propertyName; } } public object Value { get { return _value; } } public CriteriaOperator criteriaOperator { get { return _criteriaOperator; } } } }
namespace Chap7.QueryObject.Infrastructure { /// <summary> /// OrderByClause类标识用于查询的排序属性。 /// </summary> public class OrderByClause { public string PropertyName { get; set; } public bool Desc { get; set; } } }
namespace Chap7.QueryObject.Infrastructure { /// <summary> /// 使用另一个枚举来确定如何将Criterion对象一起评估。 /// </summary> public enum QueryOperator { And, Or } }
namespace Chap7.QueryObject.Infrastructure { /// <summary> /// 有时复杂的查询非常难以创建,在这种情况下,可以使用指向数据库视图或存储过程的命名查询。这些命名查询将以枚举的形式添加。 /// </summary> public enum QueryName { Dynamic = 0, RetrieveOrderUsingAComplexQuery = 1 } }
using System; using System.Collections.Generic; namespace Chap7.QueryObject.Infrastructure { /// <summary> /// Query类将Query Object模式组合在一起。在这个类中,包含了一组Criterion对象,一个QueryName对象以及一个QueryOperator值。Query类也包含了一个IsNamedQuery方法,用来判断该查询是否已经动态生成或与 /// Repository中某个预先建立的查询相关。 /// </summary> public class Query { QueryName _name; IList<Criterion> _criteria; public Query() : this(QueryName.Dynamic, new List<Criterion>()) { } public Query(QueryName name,List<Criterion> criteria) { _name = name; _criteria = criteria; } public QueryName Name { get { return _name; } } public bool IsNamedQuery() { return Name != QueryName.Dynamic; } public IEnumerable<Criterion> Criteria { get { return _criteria; } } public void Add(Criterion criterion) { if (!IsNamedQuery()) { _criteria.Add(criterion); } else { throw new ApplicationException(); } } public QueryOperator QueryOperator { get; set; } public OrderByClause OrderByClause { get; set; } public void TranslateInto(System.Data.SqlClient.SqlCommand command) { } } }
using System; using System.Collections.Generic; namespace Chap7.QueryObject.Infrastructure { /// <summary> /// NamedQueryFactory类为命名查询创建一个Query Object。Query Translator能够检查这个Query Object,以确定它是不是一个命名查询并将Criterion用作存储的数据库查询的值。 /// </summary> public class NamedQueryFactory { public static Query CreateRetrieveOrdersUsingAComplexQuery(Guid CustomerId) { List<Criterion> criteria = new List<Criterion>(); Query query = new Query(QueryName.RetrieveOrderUsingAComplexQuery, criteria); criteria.Add(new Criterion("CustomerId", CustomerId, CriteriaOperator.NotApplicable)); return query; } } }
依托上面的这几个类,就完成了Query Object模式的实现。
注意:在Query Objec模式中并没有子查询的概念。为了提供子查询,只需要将一组Query Object对象添加到Query Object中。但是如果需要用到子查询或其他稍微复杂的查询,通常更好的做法是使用Repository或数据库中的存储查询。
创建一个简单的领域模型来演示如何使用所创建的Query Object实现。
using System; namespace Chap7.QueryObject.Infrastructure { public class Order { public Guid CustomerId { get; set; } public DateTime OrderDate { get; set; } public Guid Id { get; set; } } }
/// <summary> /// 为之前创建的Order类,创建仓储接口 /// </summary> public interface IOrderRepository { IEnumerable<Order> FindBy(Query query); }
public class OrderService { IOrderRepository _orderRepository; public OrderService(IOrderRepository orderRepository) { _orderRepository = orderRepository; } /// <summary> /// 通过Criterion和OrderByClause添加来创建动态查询。 /// </summary> /// <param name="customerId"></param> /// <returns></returns> public IEnumerable<Order> FindAllCustomerOrderBy(Guid customerId) { IEnumerable<Order> customerOrders = new List<Order>(); Query query = new Query(); query.Add(new Criterion("CustomerId", customerId, CriteriaOperator.Equal)); query.OrderByClause = new OrderByClause { PropertyName = "CustomerId", Desc = true }; customerOrders = _orderRepository.FindBy(query); return customerOrders; } /// <summary> /// 通过Criterion和OrderByClause添加来创建动态查询。 /// </summary> /// <param name="customerId"></param> /// <param name="orderDate"></param> /// <returns></returns> public IEnumerable<Order> FindAllCustomerOrdersWithInOrderDateBy(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.OrderByClause = new OrderByClause { PropertyName = "OrderDate", Desc = true }; customerOrders = _orderRepository.FindBy(query); return customerOrders; } /// <summary> /// 命名查询,使用NamedQueryFactory来创建要传给Repository和Query Object。 /// </summary> /// <param name="customerId"></param> /// <returns></returns> public IEnumerable<Order> FindAllCustomersOrdersUsingAComplexQueryWith(Guid customerId) { IEnumerable<Order> customerOrders = new List<Order>(); Query query = NamedQueryFactory.CreateRetrieveOrdersUsingAComplexQuery(customerId); customerOrders = _orderRepository.FindBy(query); return customerOrders; } }
上面的这三个类属于实体的范畴,所以在Model下创建这些类。
using System; using System.Linq; using System.Text; using Chap7.QueryObject.Infrastructure; using System.Data.SqlClient; using System.Data; namespace Chap7.QueryObject.Repository { public static class OrderQueryTranslator { static string baseSelectQuery = "select * from orders"; /// <summary> /// TranslateInfo方法首先要标识Query Object是否为命名查询,如果是:则将该命令设置为一个存储过程,该存储过程的名称为查询枚举名称。然后Query和Criterion提供存储过程所需要的所有参数。 /// 如果是动态创建的。则需要遍历所有Criterion并构建SQL语句,使用各种方法将Order实体的属性名称转化为Order列表的列名。在该Demo中,数据表列和实体属性之间是存在一对一映射的。实际中可能不是这样的, /// 领域模型可能随时会变。 /// </summary> /// <param name="query"></param> /// <param name="command"></param> public static void TranslateInfo(this Query query, SqlCommand command) { if (query.IsNamedQuery()) { command.CommandType = CommandType.StoredProcedure; command.CommandText = query.Name.ToString(); } else { StringBuilder sqlQuery = new StringBuilder(); sqlQuery.Append(baseSelectQuery); bool _isNonfirstFilterClause = false; if (query.Criteria.Count() > 0) { sqlQuery.Append(" where "); } foreach (Criterion criterion in query.Criteria) { if (!_isNonfirstFilterClause) sqlQuery.Append(GetQueryOperator(query)); sqlQuery.Append(AddFilterClauseFrom(criterion)); _isNonfirstFilterClause = true; } sqlQuery.Append(GenerateOrderByClauseFrom(query.OrderByClause)); command.CommandType = CommandType.Text; command.CommandText = sqlQuery.ToString(); } } private static string GetQueryOperator(Query query) { if (query.QueryOperator == QueryOperator.And) return "AND "; else return "OR "; } private static string GenerateOrderByClauseFrom(OrderByClause orderByClause) { return string.Format(" order by {0} {1}", FindTableColumnFor(orderByClause.PropertyName), orderByClause.Desc ? "DESC" : "ASC"); } 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"); } } private static string AddFilterClauseFrom(Criterion criterion) { return string.Format("{0}{1}@{2}", FindTableColumnFor(criterion.PorpertyName), FindSqlOperatorFor(criterion.criteriaOperator), criterion.PorpertyName); } private static string FindSqlOperatorFor(CriteriaOperator criteriaOperator) { switch (criteriaOperator) { case CriteriaOperator.Equal: return "="; case CriteriaOperator.LessThanOrEqual: return "<="; default: throw new ApplicationException("No operator defined."); } } } }
public class OrdeRepository : IOrderRepository { string _conStr; public OrdeRepository(string conStr) { _conStr = conStr; } public IEnumerable<Order> FindBy(Query query) { IList<Order> orders = new List<Order>(); using (SqlConnection connection = new SqlConnection(_conStr)) { 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; } }
以上是关于Query Object的主要内容,如果未能解决你的问题,请参考以下文章
PHP Fatal error: Call to a member function query() on a non-object in F 代码如下
带有快速错误的 GraphQL:Query.example 字段类型必须是输出类型,但得到:[object Object]