zoukankan      html  css  js  c++  java
  • 企业应用架构模式——QueryObject模式

    场景

    比如我们有个Employee实体,属性有 EmployeeID(雇员id)、NationalIDNumber(雇员标志号,如身份证)、ManagerID(雇员上司id)、Title(职位)、MaritalStatus(婚姻状态)、Gender(性别)、HireDate(雇佣日期)、SalariedFlag(工作性质,计时或月薪)。我们写数据库层时经常会遇到这样的需求:

    查询标志号为"001”的雇员

    public class EmployeeRepository : IRepository<Employee>
       {
          public Employee Find(object id)
          {}
       }

    查询计时或月薪的雇员

    public class EmployeeRepository : IRepository<Employee>
       {
          public IEnumerable<Employee> FindSalariedEmployee(SalariedFlag salariedFlag)
          {}
       }

    可以看出只要我们有通过对象属性来查询对象,就要在相应的数据层增加对应的Findxxx方法,不过这只是单一的查询,如果存在组合条件的查询,Employee仓储中会充斥大量的检索方法。

    QueryObject模式

    QueryObject(查询对象)是一个解释器,也就是说,他是由多个对象组成的结构,该结构可以自身转化为一个sql查询。可以通过引用类的和属性来创建对应数据库表和列的查询。

    查询对象的好处

    1、完全将底层数据库的查询语言抽象出来,因此将数据库持久化和检索的基础设施关注点从业务层中分离出来。

    2、对于多个数据库,通过设计查询对象,根据运行的数据库来产生不同的sql,屏蔽了各种不同sql语法中的差别。

    3、消除对数据库的查询冗余,如果在先前的会话中运行过相同的查询,可以使用查询对象从标志映射中选取对象。

    QueryObject 模型 类图

    实现QueryObject 模式

    首先创建一个枚举 CriteriaOperator 用来处理查询条件的映射

       1:  public enum CriteriaOperator
       2:  {
       3:      Equal,// =
       4:      LessThanOrEqual,// <=
       5:      NotApplicable, // ≠
       6:      LessThan, // <
       7:      GreaterThan, // >
       8:      GreaterThanOrEqual, // >=
       9:      Like // %
      10:  }

    创建查询过滤器的类Criterion

       1:  /// <summary>
       2:  /// 查询过滤器
       3:  /// </summary>
       4:  public class Criterion
       5:  {
       6:      private string _propertyName;//实体属性
       7:      private object _value;//进行比较的值
       8:      private CriteriaOperator _criteriaOperator;//比较操作符 
       9:   
      10:      public Criterion(string propertyName, object value, CriteriaOperator criteriaOperator)
      11:      {
      12:          _propertyName = propertyName;
      13:          _value = value;
      14:          _criteriaOperator = criteriaOperator;
      15:      }
      16:   
      17:      public string PropertyName
      18:      {
      19:          get { return _propertyName; }
      20:      }
      21:   
      22:      public object Value 
      23:      {
      24:          get { return _value; }
      25:      }
      26:      public CriteriaOperator CriteriaOperator
      27:      {
      28:          get { return _criteriaOperator; }
      29:      }
      30:   
      31:  }

    创建一个用于表示排序属性的类OrderByClause

       1:  public class OrderByClause
       2:  {
       3:      public string PropertyName { get; set; }//属性名对应数据库表的列名
       4:   
       5:      public bool Desc { get; set; }//desc or asc
       6:  }

    创建另一个枚举QueryOperator,用来表示条件的连字符 and 或者 or

       1:  public enum QueryOperator
       2:  {
       3:      And,
       4:      Or
       5:  }

    有时候复杂的查询对象难以构建,我们会通过存储过程或视图来处理此种情况,需要构建一个枚举QueryName用来指示是存储过程(视图)还是构建动态的sql语句

       1:  public enum QueryName
       2:  {
       3:      Dynamic = 0, //动态创建
       4:      RetrieveOrdersUsingAComplexQuery = 1//使用已经创建好了的存储过程、视图、特别是查询比较复杂时使用存储过程
       5:  }

    最后创建Query类,用来组合查询条件(Criterion)、查询条件连字符(QueryOperator),排序(OrderByClause)

       1:  public class Query
       2:  {
       3:      private QueryName _name;
       4:   
       5:      private IList<Criterion> _criteria;//查询条件
       6:   
       7:      public Query():this(QueryName.Dynamic,new List<Criterion>())
       8:      { }
       9:      public Query(QueryName name, IList<Criterion> criteria)
      10:      {
      11:          _name = name;
      12:          _criteria = criteria;
      13:      }
      14:   
      15:      public QueryName Name
      16:      {
      17:          get { return _name; }
      18:      }
      19:   
      20:      /// <summary>
      21:      /// 判断是存储过程还是动态sql
      22:      /// </summary>
      23:      /// <returns></returns>
      24:      public bool IsNamedQuery()
      25:      {
      26:          return Name != QueryName.Dynamic;
      27:      }
      28:   
      29:      public IEnumerable<Criterion> Criteria
      30:      {
      31:          get { return _criteria; }
      32:      }
      33:   
      34:      public void Add(Criterion criterion)
      35:      {
      36:          if (!IsNamedQuery())
      37:          {
      38:              _criteria.Add(criterion);
      39:          }
      40:          else
      41:          {
      42:              throw new ApplicationException("You cannot add additional criteria to named queries");
      43:          }
      44:      }
      45:      //查询条件连字符
      46:      public QueryOperator QueryOperator
      47:      {
      48:          get;
      49:          set;
      50:      }
      51:      //排序列
      52:      public OrderByClause OrderByProperty
      53:      {
      54:          get;
      55:          set;
      56:      }
      57:   
      58:  }

    使用QueryObject模式

    仓储层(Repository)接口

       1:  public interface IRepository<T>
       2:  {
       3:      int Add(T entity);
       4:      int Save(T entity);
       5:      void Delete(T entity);
       6:      T Find(object id);
       7:      T Find(Query query);
       8:      IEnumerable<T> FindBy(Query query);
       9:      IEnumerable<T> FindBy(Query query, int pageSize, int pageCount);
      10:  }

    创建一个翻译器,将查询对象翻译成sql语句

       1:  public static class AdmissionTicketOrderQueryTranslator
       2:  {
       3:      private static readonly string baseSelectQuery = "SELECT * FROM AdmissionTicketOrder";
       4:   
       5:      public static void TranslateInfo(this Query query, SqlCommand cmd)
       6:          {
       7:              if (query.IsNamedQuery())
       8:              {
       9:                  cmd.CommandType = CommandType.StoredProcedure;
      10:                  cmd.CommandText = query.Name.ToString();
      11:                  foreach (Criterion criteria in query.Criteria)
      12:                  {
      13:                      cmd.Parameters.Add(new SqlParameter("@" + criteria.PropertyName, criteria.Value));
      14:                  }
      15:              }
      16:              else
      17:              {
      18:                  StringBuilder sqlQuery = new StringBuilder();
      19:                  sqlQuery.Append(baseSelectQuery);
      20:                  bool _isNotfirstFilterClause = false;
      21:   
      22:                  if (query.Criteria.Count()>0)
      23:                  {
      24:                      sqlQuery.Append(" WHERE ");
      25:                  }
      26:   
      27:                  foreach (Criterion criterion in query.Criteria)
      28:                  {
      29:                      if (_isNotfirstFilterClause)
      30:                      {
      31:                          sqlQuery.Append(GetQueryOperator(query));
      32:                      }
      33:                      sqlQuery.Append(AddFilterClauseFrom(criterion));
      34:                      cmd.Parameters.Add(new SqlParameter("@" + criterion.PropertyName, criterion.Value));
      35:                      _isNotfirstFilterClause = true;
      36:                  }
      37:                  sqlQuery.Append(GenerateOrderByClauseFrom(query.OrderByProperty));
      38:                  cmd.CommandType = CommandType.Text;
      39:                  cmd.CommandText = sqlQuery.ToString();
      40:              }
      41:          }
      42:   
      43:      private static string GenerateOrderByClauseFrom(OrderByClause orderByClause)
      44:          {
      45:              return String.Format(" ORDER BY {0} {1}",
      46:                  FindTableColumnFor(orderByClause.PropertyName), orderByClause.Desc ? "DESC" : "ASC");
      47:          }
      48:   
      49:      /// <summary>
      50:          /// 
      51:          /// </summary>
      52:          /// <param name="query"></param>
      53:          /// <returns></returns>
      54:      private static string GetQueryOperator(Query query)
      55:          {
      56:              if (query.QueryOperator==QueryOperator.And)
      57:              {
      58:                  return " AND ";
      59:              }
      60:              else
      61:              {
      62:                  return " OR ";
      63:              }
      64:          }
      65:   
      66:      private static string AddFilterClauseFrom(Criterion criterion)
      67:          {
      68:              return string.Format("{0} {1} @{2} ", FindTableColumnFor(criterion.PropertyName), FindSQLOperatorFor(criterion.CriteriaOperator), criterion.PropertyName);
      69:          }
      70:   
      71:      private static string FindSQLOperatorFor(CriteriaOperator criteriaOperator)
      72:          {
      73:              switch (criteriaOperator)
      74:              {
      75:                  case CriteriaOperator.Equal:
      76:                      return "=";
      77:                  case CriteriaOperator.LessThanOrEqual:
      78:                      return "<=";
      79:                  case CriteriaOperator.NotApplicable:
      80:                      return "<>";
      81:                  case CriteriaOperator.LessThan:
      82:                      return "<";
      83:                  case CriteriaOperator.GreaterThan:
      84:                      return ">";
      85:                  case CriteriaOperator.GreaterThanOrEqual:
      86:                      return ">=";
      87:                  default:
      88:                      throw new ApplicationException("No operator defined.");
      89:              }
      90:          }
      91:   
      92:      private static string FindTableColumnFor(string propertyName)
      93:          {
      94:              switch (propertyName)
      95:              {
      96:                  case "ID":
      97:                      return "ID";
      98:                  case "OrderID":
      99:                      return "OrderID";
     100:                  case "TicketId":
     101:                      return "TicketId";
     102:                  case "OrderUserName":
     103:                      return "OrderUserName";
     104:                  case "OrderUserSex":
     105:                      return "OrderUserSex";
     106:                  case "OrderUserPhone":
     107:                      return "OrderUserPhone";
     108:                  case "ProviderContactName":
     109:                      return "ProviderContactName";
     110:                  case "TicetUseDateTime":
     111:                      return "TicetUseDateTime";
     112:                  case "SalePrice":
     113:                      return "SalePrice";
     114:                  case "BuyPrice":
     115:                      return "BuyPrice";
     116:                  case "BuyAmount":
     117:                      return "BuyAmount";
     118:                  case "TicketType":
     119:                      return "TicketType";
     120:                  default:
     121:                      throw new ApplicationException("No column defined for this property.");
     122:              }
     123:          }
     124:   
     125:  }
     126:   

    仓储实现层

       1:  public class AdmissionTicketOrderRepository : IRepository<AdmissionTicketOrder>
       2:  {
       3:      private SqlHelper _sqlHelper;
       4:   
       5:      public AdmissionTicketOrderRepository(SqlHelper sqlHelper)
       6:      {
       7:          _sqlHelper = sqlHelper;
       8:      }
       9:   
      10:      private static readonly string insertOrderExtendSql = "sp_InsertOrUpdateAdmissionTicketOrder";
      11:      public int Add(AdmissionTicketOrder entity)
      12:          {
      13:              using (var cmd = _sqlHelper.PrepareStoredSqlCommand(insertOrderExtendSql))
      14:              {
      15:                  cmd.AddParam("@ID", entity.ID);
      16:                  cmd.AddParam("@OrderID", entity.OrderID);
      17:                  cmd.AddParam("@TicketId", entity.TicketId);
      18:                  cmd.AddParam("@OrderUserName", entity.OrderUserName);
      19:                  cmd.AddParam("@OrderUserSex", entity.OrderUserSex);
      20:                  cmd.AddParam("@OrderUserPhone", entity.OrderUserPhone);
      21:                  cmd.AddParam("@ProviderContactName", entity.ProviderContactName);
      22:                  cmd.AddParam("@ProviderContactPhone", entity.ProviderContactPhone);
      23:                  cmd.AddParam("@TicetUseDateTime", entity.TicetUseDateTime);
      24:                  cmd.AddParam("@IsNeed", entity.IsNeed);
      25:                  cmd.AddParam("@BillAddress", entity.BillAddress);
      26:                  cmd.AddParam("@BuyPrice", entity.BuyPrice);
      27:                  cmd.AddParam("@SalePrice", entity.SalePrice);
      28:                  cmd.AddParam("@BuyAmount", entity.BuyAmount);
      29:                  cmd.AddParam("@GetTicketAddress", entity.GetTicketAddress);
      30:                  cmd.AddParam("@TicketType", entity.TicketType);
      31:                  var parm = cmd.AddReturnValueParameter("ReturnValue");
      32:                  cmd.ExecuteNonQuery();
      33:                  return (int)parm.Value;
      34:              }
      35:          }
      36:   
      37:      public int Save(AdmissionTicketOrder entity)
      38:          {
      39:              return Add(entity);
      40:          }
      41:   
      42:      private static readonly string deleteOrder = "DELETE AdmissionTicketOrder WHERE ID=@Id";
      43:      public void Delete(AdmissionTicketOrder entity)
      44:          {
      45:              using (var cmd = _sqlHelper.PrepareTextSqlCommand(deleteOrder))
      46:              {
      47:                  cmd.AddParam("@Id", entity.ID);
      48:                  cmd.ExecuteNonQuery();
      49:              }
      50:          }
      51:   
      52:      private static readonly string selectOrder = "SELECT * FROM AdmissionTicketOrder WHERE ID=@Id";
      53:      public AdmissionTicketOrder Find(object id)
      54:          {
      55:              using (var cmd = _sqlHelper.PrepareTextSqlCommand(selectOrder))
      56:              {
      57:                  cmd.AddParam("@Id", id);
      58:                  using (var reader = cmd.ExecuteReader())
      59:                  {
      60:                      if (reader.Read())
      61:                      {
      62:                          return (new AdmissionTicketOrder
      63:                          {
      64:                              Id = reader.GetValue<int>("Id"),
      65:                              OrderID = reader.GetValue<int>("OrderID"),
      66:                              TicketId=reader.GetValue<int>("TicketId"),
      67:                              OrderUserName=reader.GetValue<string>("OrderUserName"),
      68:                              OrderUserSex=reader.GetValue<int>("OrderUserSex"),
      69:                              OrderUserPhone = reader.GetValue<string>("OrderUserPhone"),
      70:                              ProviderContactName=reader.GetValue<string>("ProviderContactName"),
      71:                              ProviderContactPhone=reader.GetValue<string>("ProviderContactPhone"),
      72:                              TicetUseDateTime=reader.GetValue<DateTime>("TicetUseDateTime"),
      73:                              IsNeed=reader.GetValue<bool>("IsNeed"),
      74:                              BillAddress=reader.GetValue<string>("BillAddress"),
      75:                              SalePrice = reader.GetValue<decimal>("SalePrice"),
      76:                              BuyPrice = reader.GetValue<decimal>("BuyPrice"),
      77:                              BuyAmount=reader.GetValue<decimal>("BuyAmount"),
      78:                              GetTicketAddress = reader.GetValue<string>("GetTicketAddress"),
      79:                              TicketType=reader.GetValue<string>("TicketType")
      80:                          });
      81:                      }
      82:                      return null;
      83:                  }
      84:              }
      85:          }
      86:   
      87:      public AdmissionTicketOrder Find(Query query)
      88:          {
      89:              SqlCommand cmd = _sqlHelper.PrepareSqlCommand();
      90:              query.TranslateInfo(cmd);
      91:              using (var reader = cmd.ExecuteReader())
      92:              {
      93:                  if (reader.Read())
      94:                  {
      95:                      return (new AdmissionTicketOrder
      96:                      {
      97:                          Id = reader.GetValue<int>("Id"),
      98:                          OrderID = reader.GetValue<int>("OrderID"),
      99:                          TicketId = reader.GetValue<int>("TicketId"),
     100:                          OrderUserName = reader.GetValue<string>("OrderUserName"),
     101:                          OrderUserSex = reader.GetValue<int>("OrderUserSex"),
     102:                          OrderUserPhone = reader.GetValue<string>("OrderUserPhone"),
     103:                          ProviderContactName = reader.GetValue<string>("ProviderContactName"),
     104:                          ProviderContactPhone = reader.GetValue<string>("ProviderContactPhone"),
     105:                          TicetUseDateTime = reader.GetValue<DateTime>("TicetUseDateTime"),
     106:                          IsNeed = reader.GetValue<bool>("IsNeed"),
     107:                          BillAddress = reader.GetValue<string>("BillAddress"),
     108:                          SalePrice = reader.GetValue<decimal>("SalePrice"),
     109:                          BuyPrice = reader.GetValue<decimal>("BuyPrice"),
     110:                          BuyAmount = reader.GetValue<decimal>("BuyAmount"),
     111:                          GetTicketAddress = reader.GetValue<string>("GetTicketAddress"),
     112:                          TicketType = reader.GetValue<string>("TicketType")
     113:                      });
     114:                  }
     115:                  return null;
     116:              }
     117:          }
     118:   
     119:      public IEnumerable<AdmissionTicketOrder> FindBy(Query query)
     120:          {
     121:              IList<AdmissionTicketOrder> admissionTicketOrders = new List<AdmissionTicketOrder>();
     122:              SqlCommand cmd = _sqlHelper.PrepareSqlCommand();
     123:              query.TranslateInfo(cmd);
     124:              using (SqlDataReader reader = cmd.ExecuteReader())
     125:              {
     126:                  while (reader.Read())
     127:                  {
     128:                      admissionTicketOrders.Add(
     129:                          new AdmissionTicketOrder
     130:                          {
     131:                              Id = reader.GetValue<int>("Id"),
     132:                              OrderID = reader.GetValue<int>("OrderID"),
     133:                              TicketId = reader.GetValue<int>("TicketId"),
     134:                              OrderUserName = reader.GetValue<string>("OrderUserName"),
     135:                              OrderUserSex = reader.GetValue<int>("OrderUserSex"),
     136:                              OrderUserPhone = reader.GetValue<string>("OrderUserPhone"),
     137:                              ProviderContactName = reader.GetValue<string>("ProviderContactName"),
     138:                              ProviderContactPhone = reader.GetValue<string>("ProviderContactPhone"),
     139:                              TicetUseDateTime = reader.GetValue<DateTime>("TicetUseDateTime"),
     140:                              IsNeed = reader.GetValue<bool>("IsNeed"),
     141:                              BillAddress = reader.GetValue<string>("BillAddress"),
     142:                              SalePrice = reader.GetValue<decimal>("SalePrice"),
     143:                              BuyPrice = reader.GetValue<decimal>("BuyPrice"),
     144:                              BuyAmount = reader.GetValue<decimal>("BuyAmount"),
     145:                              GetTicketAddress = reader.GetValue<string>("GetTicketAddress"),
     146:                              TicketType = reader.GetValue<string>("TicketType")
     147:                          }
     148:                      );
     149:                  }
     150:              }
     151:              return admissionTicketOrders;
     152:          }
     153:   
     154:      public IEnumerable<AdmissionTicketOrder> FindBy(Query query, int pageSize, int pageCount)
     155:      {
     156:          return FindBy(query).Skip(pageSize * pageCount).Take(pageCount);
     157:      }
     158:  }

    控制台调用

       1:  static void Main(string[] args)
       2:  {
       3:      using (var sqlHelper = new SqlHelper("ConnectionString"))
       4:      {
       5:          AdmissionTicketOrderRepository ator = new AdmissionTicketOrderRepository(sqlHelper);
       6:          AdmissionTicketOrder ato = ator.Find(2);
       7:   
       8:          Query query = new Query();
       9:   
      10:          query.Add(new Criterion("SalePrice", 100, CriteriaOperator.GreaterThan));
      11:          query.QueryOperator = QueryOperator.And;
      12:          query.Add(new Criterion("TicketType", "单一票型", CriteriaOperator.Equal));
      13:          query.Add(new Criterion("BuyAmount", 250, CriteriaOperator.LessThan));
      14:          query.QueryOperator = QueryOperator.And;
      15:          query.OrderByProperty = new OrderByClause { PropertyName = "TicetUseDateTime", Desc = true };
      16:   
      17:          List<AdmissionTicketOrder> list = ator.FindBy(query).ToList();
      18:          foreach (var item in list)
      19:          {
      20:              Console.WriteLine("{0}{1}{2}{3}",item.ID,item.ProviderContactName,item.BuyPrice,item.SalePrice);
      21:          }
      22:      }
      23:      Console.Read();
      24:      
      25:  }

    源代码下载

  • 相关阅读:
    Node.js理解
    PayPal为什么从Java迁移到Node.js
    移动开发技巧总结
    Flex性能调优相关的一些总结
    Flex组件的生命周期
    Adobe Flash Builder 4.7 新功能详解
    【Django】Cookie
    【Django】路由系统
    【Django】视图系统
    【Django】ORM操作#2
  • 原文地址:https://www.cnblogs.com/whx1973/p/2838666.html
Copyright © 2011-2022 走看看