zoukankan      html  css  js  c++  java
  • 对List进行子查询及分组

        在FaibClass.Data中,DataModelList<T> : List<T>类有三个方法:Select、Group和Compute,可以对集合进行子查询及分组统计,今天就说一其原理。
        本人也看过DataTable的Select方法,不过才浅得很,感觉很是复杂,所以变通了一下,使用动态编译的方式来解决这个问题。
        首先,这两个方法都有一个共同的参数QueryBuilder,使用其构造方法new QueryBuilder(true),用于在添加查询条件时,添加对应的C#语法语句,再动态的生成代码查询出所要的结果。比如添加条件 
        QueryBuilder qb = new QueryBuilder(true);
        qb.Append(QueryRelation.And, QueryCompare.Greater, TB_BUY_BILL._BUY_TIME, new DateTime(2009, 4, 29));
        qb.Append(QueryRelation.And, QueryCompare.Equal, TB_BUY_BILL._BILL_STATE, true);
    时,已经生成了一条C#语句
        {0}.BUY_TIME > DateTime.Parse("2009, 4, 29") and {0}.BILL_STATE == true
        其中的{0}要在代码编译时才发挥作用,用实体对象替换。

        一、Select方法

            /// <summary>
            
    /// 获取按照指定的与筛选条件相匹配的子集合。
             
    /// </summary>
            
    /// <param name="queryBuilder">要用来筛选的条件。</param>
            
    /// <returns></returns>
            public DataModelList<T> Select(QueryBuilder queryBuilder)
            {
                
    object result = new ExpressionCompiler().ComplieSelect(this, queryBuilder);
                
    if (result == nullreturn null;
                
    return (DataModelList<T>)result;
            }

    ExpressionCompiler是一个表达示编译类,ComplieSelect的方法如下:
     1         /// <summary>
     2         /// 编译查询子集合表达示。
     3         /// </summary>
     4         /// <param name="list">进行筛选的数据集合。</param>
     5         /// <param name="queryBuilder">过滤查询器。</param>
     6         /// <returns></returns>
     7         internal IDataModelList ComplieSelect(IDataModelList list, QueryBuilder queryBuilder)
     8         {
     9             if (queryBuilder == nullreturn list;
    10             if (queryBuilder.expressionBuilder.Length == 0return list;
    11             StringBuilder code = new StringBuilder();
    12             string expression = FormatExpression(list.ModelType, queryBuilder);
    13             code.Append(@"
    14                 public IDataModelList Execute(IList list)
    15                 {
    16                     //创建子集合对象
    17                     IDataModelList result = (IDataModelList)Activator.CreateInstance(list.GetType());
    18                     //循环当前集合
    19                     foreach(object item in list)
    20                     {
    21                         BaseModel model = (BaseModel)item;
    22                         //判断表达示,如果为真添加到子集合
    23                         if (" + expression + @")
    24                         {
    25                             result.Add(model);
    26                         }
    27                     }
    28                     return result;
    29                 }
    30             ");
    31             //执行编译后返回结果
    32             object result = CompileCode("ComplieSelect", code.ToString(), list);
    33             if (result == nullreturn null;
    34             return (IDataModelList)result;
    35         }

    FormatExpression即是将{0}替换为实体对象:
     1         /// <summary>
     2         /// 格式化表达示。
     3         /// </summary>
     4         /// <param name="type"></param>
     5         /// <param name="queryBuilder"></param>
     6         /// <returns></returns>
     7         private string FormatExpression(Type type, QueryBuilder queryBuilder)
     8         {
     9             //将'号转为"
    10             string expression = queryBuilder.expressionBuilder.ToString().Replace("'""\"");
    11             //替换{0}为Model.GetValue
    12             foreach (PropertyInfo pinfo in type.GetProperties())
    13             {
    14                 if (expression.IndexOf("{0}." + pinfo.Name) != -1)
    15                 {
    16                     //布尔
    17                     if (pinfo.PropertyType == typeof(Boolean))
    18                     {
    19                         expression = expression.Replace("{0}." + pinfo.Name, "((bool)model.GetValue(\"" + pinfo.Name + "\") ? 1 : 0)");
    20                     }
    21                     //枚举
    22                     else if (pinfo.PropertyType.BaseType == typeof(Enum))
    23                     {
    24                         expression = expression.Replace("{0}." + pinfo.Name, "(int)model.GetValue(\"" + pinfo.Name + "\")");
    25                     }
    26                     else
    27                     {
    28                         expression = expression.Replace("{0}." + pinfo.Name, "(" + pinfo.PropertyType + ")model.GetValue(\"" + pinfo.Name + "\")");
    29                     }
    30                 }
    31             }
    32             return expression;
    33         }

        二、Group方法。
        首先定义了一个类及集合来保存分组的结果:
     1 using System;
     2 using System.Collections.Generic;
     3 
     4 namespace FaibClass.Data
     5 {
     6     /// <summary>
     7     /// 数据分组集合。
     8     /// </summary>
     9     public class DataGroupCollection : List<DataGroup>
    10     {
    11         /// <summary>
    12         /// 索引器。
    13         /// </summary>
    14         /// <param name="value">分组的值。</param>
    15         /// <returns></returns>
    16         public DataGroup this[object value]
    17         {
    18             get 
    19             {
    20                 foreach (DataGroup group in this)
    21                 {
    22                     if (group.Value.Equals(value))
    23                         return group;
    24                 }
    25                 return null;
    26             }
    27         }
    28 
    29         /// <summary>
    30         /// 检查分组值是否存在于本集合中。
    31         /// </summary>
    32         /// <param name="value">分组的值。</param>
    33         /// <returns></returns>
    34         public bool ContainsKey(object value)
    35         {
    36             foreach(DataGroup group in this)
    37             {
    38                 if (group.Value.Equals(value))
    39                     return true;
    40             }
    41             return false;
    42         }
    43 
    44         /// <summary>
    45         /// 添加一个数据分组。
    46         /// </summary>
    47         /// <param name="value">分组的值。</param>
    48         /// <param name="list">分组后的集合。</param>
    49         public void Add(object value, IDataModelList list)
    50         {
    51             DataGroup group = new DataGroup();
    52             group.Value = value;
    53             group.List = list;
    54             base.Add(group);
    55         }
    56     }
    57 
    58     /// <summary>
    59     /// 数据分组。
    60     /// </summary>
    61     public class DataGroup
    62     {
    63         private object value;
    64         private IDataModelList list;
    65 
    66         /// <summary>
    67         /// 值。
    68         /// </summary>
    69         public object Value
    70         {
    71             get { return value; }
    72             set { this.value = value; }
    73         }
    74 
    75         /// <summary>
    76         /// 数据集合。
    77         /// </summary>
    78         public IDataModelList List
    79         {
    80             get { return list; }
    81             set { list = value; }
    82         }
    83     }
    84 }
    85 

    下面是Compute方法的原型:
     1         /// <summary>
     2         /// 对当前数据集合进行分组。
     3         /// </summary>
     4         /// <param name="memberName">参照分组的字段名称。</param>
     5         /// <returns></returns>
     6         public DataGroupCollection Group(string memberName)
     7         {
     8             PropertyInfo pinfo = ModelType.GetProperty(memberName, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
     9             if (pinfo == null)
    10             {
    11                 throw new InvalidColumnException(memberName);
    12             }
    13             DataGroupCollection coll = new DataGroupCollection();
    14             foreach (T t in this)
    15             {
    16                 BaseModel model = (BaseModel)t;
    17                 object value = model.GetValue(memberName);
    18                 if (!coll.ContainsKey(value))
    19                 {
    20                     coll.Add(value, new DataModelList<T>());
    21                 }
    22                 coll[value].List.Add(t);
    23             }
    24             return coll;
    25         }

        三、Compute方法
        首先是一个计算类别的枚举
     1     /// <summary>
     2     /// 计算类别。
     3     /// </summary>
     4     public enum ComputeType
     5     {
     6         /// <summary>
     7         /// 求个数。
     8         /// </summary>
     9         Count = 0,
    10         /// <summary>
    11         /// 求和。
    12         /// </summary>
    13         Sum,
    14         /// <summary>
    15         /// 求平均值。
    16         /// </summary>
    17         Average,
    18         /// <summary>
    19         /// 求最大值。
    20         /// </summary>
    21         Max,
    22         /// <summary>
    23         /// 求最小值。
    24         /// </summary>
    25         Min
    26     }
    一个表达示类
     1     /// <summary>
     2     /// 计算表达示。
     3     /// </summary>
     4     public class ComputeExpression
     5     {
     6         private ComputeType countType;
     7         private string field;
     8         private decimal result;
     9 
    10         /// <summary>
    11         /// 构造表达示。
    12         /// </summary>
    13         /// <param name="field">要计算的字段名。</param>
    14         /// <param name="countType">计算的类别。</param>
    15         public ComputeExpression(string field, ComputeType countType)
    16         {
    17             this.field = field;
    18             this.countType = countType;
    19         }
    20 
    21         /// <summary>
    22         /// 计算的类别。
    23         /// </summary>
    24         public ComputeType ComputeType
    25         {
    26             get { return countType; }
    27             set { countType = value; }
    28         }
    29 
    30         /// <summary>
    31         /// 要计算的字段名。
    32         /// </summary>
    33         public string Field
    34         {
    35             get { return field; }
    36             set { field = value; }
    37         }
    38 
    39         /// <summary>
    40         /// 返回计算的结果。
    41         /// </summary>
    42         public decimal Result
    43         {
    44             get { return result; }
    45             set { result = value; }
    46         }
    47     }

    Compute方法原型:
     1         /// <summary>
     2         /// 计算用来传递筛选条件的当前集合的给定表达式。
     3         /// </summary>
     4         /// <param name="expression">要计算的表达式。</param>
     5         /// <param name="queryBuilder">要限制在表达式中进行计算的筛选器。</param>
     6         public void Compute(ComputeExpression[] expression, QueryBuilder queryBuilder)
     7         {
     8             if (expression == nullreturn;
     9             new ExpressionCompiler().ComplieCompute(this, expression, queryBuilder);
    10         }
    11 

    再来看看编译代码的ComplieCompute方法:
     1         /// <summary>
     2         /// 编译计算统计表达示。
     3         /// </summary>
     4         /// <param name="list"></param>
     5         /// <param name="expressionArray"></param>
     6         /// <param name="queryBuilder"></param>
     7         internal void ComplieCompute(IDataModelList list, ComputeExpression[] expressionArray, QueryBuilder queryBuilder)
     8         {
     9             foreach (ComputeExpression exp in expressionArray)
    10             {
    11                 PropertyInfo pinfo = list.ModelType.GetProperty(exp.Field, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
    12                 if (pinfo == null)
    13                 {
    14                     throw new InvalidColumnException(exp.Field);
    15                 }
    16                 if (exp.ComputeType != ComputeType.Count)
    17                 {
    18                     if (!Utility.IsNumberType(pinfo.PropertyType))
    19                     {
    20                         throw new Exception("非数字型字段不能参与计算。");
    21                     }
    22                 }
    23             }
    24 
    25             StringBuilder code = new StringBuilder();
    26             string expression = string.Empty;
    27             //没有查询条件,则条件为true
    28             if (queryBuilder == null)
    29             {
    30                 expression = "true";
    31             }
    32             else if (queryBuilder.Length == 0)
    33             {
    34                 expression = "true";
    35             }
    36             else
    37             {
    38                 expression = FormatExpression(list.ModelType, queryBuilder);
    39             }
    40             code.Append(@"
    41                 public bool Execute(ComputeExpression[] expressionArray, IList list)
    42                 {
    43                     int count = 0;
    44                     //初始值
    45                     foreach(ComputeExpression exp in expressionArray)
    46                     {
    47                         if (exp.ComputeType == ComputeType.Min)
    48                             exp.Result = decimal.MaxValue;
    49                         else if (exp.ComputeType == ComputeType.Max)
    50                             exp.Result = decimal.MinValue;
    51                     }
    52                     foreach(object item in list)
    53                     {
    54                         BaseModel model = (BaseModel)item;
    55                         //如果条件为真
    56                         if (" + expression + @")
    57                         {
    58                             foreach(ComputeExpression exp in expressionArray)
    59                             {
    60                                 switch (exp.ComputeType)
    61                                 {
    62                                     case ComputeType.Sum:
    63                                     case ComputeType.Average:
    64                                         exp.Result += (decimal)model.GetValue(exp.Field);
    65                                         break;
    66                                     case ComputeType.Max:
    67                                         exp.Result = Math.Max((decimal)model.GetValue(exp.Field), exp.Result);
    68                                         break;
    69                                     case ComputeType.Min:
    70                                         exp.Result = Math.Min((decimal)model.GetValue(exp.Field), exp.Result);
    71                                         break;
    72                                 }
    73                             }
    74                             count ++;
    75                         }
    76                     }
    77                     foreach(ComputeExpression exp in expressionArray)
    78                     {
    79                         if (exp.ComputeType == ComputeType.Count)
    80                             exp.Result = count;
    81                         else if (exp.ComputeType == ComputeType.Average)
    82                             exp.Result = count == 0 ? 0 : exp.Result / count;
    83                     }
    84                     return true;
    85                 }
    86             ");
    87             CompileCode("ComplieCompute", code.ToString(), expressionArray, list);
    88         }
    89 
  • 相关阅读:
    在SharePoint中实现Workflow(2):创建一个Workflow
    pku1384PiggyBank(动态规划)
    pku1088滑雪(记忆性搜索)
    hdu1251统计难题(初次接触字典树)
    详细解说 STL 排序(Sort)
    pku1631Bridging signals(动态规划题+二分搜索)
    pku1157LITTLE SHOP OF FLOWERS(简单动态规划题:摆放鲜花使审美价值达到最高)
    pku1067取石子游戏(博弈)
    pku2524Ubiquitous Religions(初次接触并查集)
    pku1050To the Max(求矩阵的最大子段和)
  • 原文地址:https://www.cnblogs.com/faib/p/1497794.html
Copyright © 2011-2022 走看看