zoukankan      html  css  js  c++  java
  • 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
        }
    }
    CriteriaOperator.cs
    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; } }
        }
    }
    Criterion.cs
    namespace Chap7.QueryObject.Infrastructure
    {
       /// <summary>
        /// OrderByClause类标识用于查询的排序属性。
       /// </summary>
        public class OrderByClause
        {
            public string PropertyName { get; set; }
            public bool Desc { get; set; }
        }
    }
    OrderByClause.cs
    namespace Chap7.QueryObject.Infrastructure
    {
        /// <summary>
        /// 使用另一个枚举来确定如何将Criterion对象一起评估。
        /// </summary>
        public enum QueryOperator
        {
            And, Or
        }
    }
    QueryOperator.cs
    namespace Chap7.QueryObject.Infrastructure
    {
        /// <summary>
        /// 有时复杂的查询非常难以创建,在这种情况下,可以使用指向数据库视图或存储过程的命名查询。这些命名查询将以枚举的形式添加。
        /// </summary>
        public enum QueryName
        {
            Dynamic = 0,
            RetrieveOrderUsingAComplexQuery = 1
        }
    }
    QueryName.cs
    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)
            {
                
            }
        }
    }
    Query.cs
    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;
            }
        }
    }
    NamedQueryFactory.cs

    依托上面的这几个类,就完成了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; }
        }
    }
    Order.cs
        /// <summary>
        /// 为之前创建的Order类,创建仓储接口
        /// </summary>
        public interface IOrderRepository
        {
            IEnumerable<Order> FindBy(Query query);
        }
    IOrderRepository.cs
    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;
            }
        }
    OrderService.cs

    上面的这三个类属于实体的范畴,所以在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.");
                }
            }
        }
    }
    OrderQueryTranslator.cs
        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;
            }
        }
    OrdeRepository.cs
  • 相关阅读:
    quagga源码学习--BGP协议的初始化
    Golang pprof heap profile is empty
    python requests 配置超时及重试次数
    SVN: bdb: BDB1538 Program version 5.3 doesn't match environment version 4.7
    OpenSSL Command-Line HOWTO
    树莓派保卫战--防止SSH暴力破解
    Mac OS X Tips
    Git Tips
    SQL分组多列统计(GROUP BY后按条件分列统计)
    Show Linux Package Sort By Size
  • 原文地址:https://www.cnblogs.com/vichin/p/13127289.html
Copyright © 2011-2022 走看看