您的位置:首页 > 其它

对List进行子查询及分组

2009-06-06 20:43 253 查看
在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 == null) return 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 == null) return list;
10 if (queryBuilder.expressionBuilder.Length == 0) return 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 == null) return 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 == null) return;
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: