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