Mercurial > pub > bltoolkit
comparison Source/Data/Linq/Builder/AggregationBuilder.cs @ 0:f990fcb411a9
Копия текущей версии из github
| author | cin |
|---|---|
| date | Thu, 27 Mar 2014 21:46:09 +0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:f990fcb411a9 |
|---|---|
| 1 using System; | |
| 2 using System.Linq; | |
| 3 using System.Linq.Expressions; | |
| 4 | |
| 5 namespace BLToolkit.Data.Linq.Builder | |
| 6 { | |
| 7 using BLToolkit.Linq; | |
| 8 using Data.Sql; | |
| 9 using Reflection; | |
| 10 | |
| 11 class AggregationBuilder : MethodCallBuilder | |
| 12 { | |
| 13 public static string[] MethodNames = new[] { "Average", "Min", "Max", "Sum" }; | |
| 14 | |
| 15 protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) | |
| 16 { | |
| 17 return methodCall.IsQueryable(MethodNames); | |
| 18 } | |
| 19 | |
| 20 protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) | |
| 21 { | |
| 22 var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); | |
| 23 | |
| 24 if (sequence.SqlQuery.Select.IsDistinct || | |
| 25 sequence.SqlQuery.Select.TakeValue != null || | |
| 26 sequence.SqlQuery.Select.SkipValue != null || | |
| 27 !sequence.SqlQuery.GroupBy.IsEmpty) | |
| 28 { | |
| 29 sequence = new SubQueryContext(sequence); | |
| 30 } | |
| 31 | |
| 32 if (!sequence.SqlQuery.OrderBy.IsEmpty) | |
| 33 { | |
| 34 if (sequence.SqlQuery.Select.TakeValue == null && sequence.SqlQuery.Select.SkipValue == null) | |
| 35 sequence.SqlQuery.OrderBy.Items.Clear(); | |
| 36 else | |
| 37 sequence = new SubQueryContext(sequence); | |
| 38 } | |
| 39 | |
| 40 var context = new AggregationContext(buildInfo.Parent, sequence, methodCall); | |
| 41 var sql = sequence.ConvertToSql(null, 0, ConvertFlags.Field).Select(_ => _.Sql).ToArray(); | |
| 42 | |
| 43 if (sql.Length == 1 && sql[0] is SqlQuery) | |
| 44 { | |
| 45 var query = (SqlQuery)sql[0]; | |
| 46 | |
| 47 if (query.Select.Columns.Count == 1) | |
| 48 { | |
| 49 var join = SqlQuery.OuterApply(query); | |
| 50 context.SqlQuery.From.Tables[0].Joins.Add(join.JoinedTable); | |
| 51 sql[0] = query.Select.Columns[0]; | |
| 52 } | |
| 53 } | |
| 54 | |
| 55 context.Sql = context.SqlQuery; | |
| 56 context.FieldIndex = context.SqlQuery.Select.Add( | |
| 57 new SqlFunction(methodCall.Type, methodCall.Method.Name, sql)); | |
| 58 | |
| 59 return context; | |
| 60 } | |
| 61 | |
| 62 protected override SequenceConvertInfo Convert( | |
| 63 ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo, ParameterExpression param) | |
| 64 { | |
| 65 return null; | |
| 66 } | |
| 67 | |
| 68 class AggregationContext : SequenceContextBase | |
| 69 { | |
| 70 public AggregationContext(IBuildContext parent, IBuildContext sequence, MethodCallExpression methodCall) | |
| 71 : base(parent, sequence, null) | |
| 72 { | |
| 73 _returnType = methodCall.Method.ReturnType; | |
| 74 _methodName = methodCall.Method.Name; | |
| 75 } | |
| 76 | |
| 77 readonly string _methodName; | |
| 78 readonly Type _returnType; | |
| 79 private SqlInfo[] _index; | |
| 80 | |
| 81 public int FieldIndex; | |
| 82 public ISqlExpression Sql; | |
| 83 | |
| 84 static object CheckNullValue(object value, object context) | |
| 85 { | |
| 86 if (value == null || value is DBNull) | |
| 87 throw new InvalidOperationException(string.Format("Function {0} returns non-nullable value, but result is NULL. Use nullable version of the function instead.", context)); | |
| 88 | |
| 89 return value; | |
| 90 } | |
| 91 | |
| 92 public override void BuildQuery<T>(Query<T> query, ParameterExpression queryParameter) | |
| 93 { | |
| 94 var expr = BuildExpression(FieldIndex); | |
| 95 var mapper = Builder.BuildMapper<object>(expr); | |
| 96 | |
| 97 query.SetElementQuery(mapper.Compile()); | |
| 98 } | |
| 99 | |
| 100 public override Expression BuildExpression(Expression expression, int level) | |
| 101 { | |
| 102 return BuildExpression(ConvertToIndex(expression, level, ConvertFlags.Field)[0].Index); | |
| 103 } | |
| 104 | |
| 105 Expression BuildExpression(int fieldIndex) | |
| 106 { | |
| 107 Expression expr; | |
| 108 | |
| 109 if (_returnType.IsClass || _methodName == "Sum" || TypeHelper.IsNullableType(_returnType)) | |
| 110 { | |
| 111 expr = Builder.BuildSql(_returnType, fieldIndex); | |
| 112 } | |
| 113 else | |
| 114 { | |
| 115 expr = Builder.BuildSql( | |
| 116 _returnType, | |
| 117 fieldIndex, | |
| 118 ReflectionHelper.Expressor<object>.MethodExpressor(o => CheckNullValue(o, o)), | |
| 119 Expression.Constant(_methodName)); | |
| 120 } | |
| 121 | |
| 122 return expr; | |
| 123 } | |
| 124 | |
| 125 public override SqlInfo[] ConvertToSql(Expression expression, int level, ConvertFlags flags) | |
| 126 { | |
| 127 switch (flags) | |
| 128 { | |
| 129 case ConvertFlags.All : | |
| 130 case ConvertFlags.Key : | |
| 131 case ConvertFlags.Field : return Sequence.ConvertToSql(expression, level + 1, flags); | |
| 132 } | |
| 133 | |
| 134 throw new InvalidOperationException(); | |
| 135 } | |
| 136 | |
| 137 public override SqlInfo[] ConvertToIndex(Expression expression, int level, ConvertFlags flags) | |
| 138 { | |
| 139 switch (flags) | |
| 140 { | |
| 141 case ConvertFlags.Field : | |
| 142 return _index ?? (_index = new[] | |
| 143 { | |
| 144 new SqlInfo { Query = Parent.SqlQuery, Index = Parent.SqlQuery.Select.Add(Sql), Sql = Sql, } | |
| 145 }); | |
| 146 } | |
| 147 | |
| 148 throw new InvalidOperationException(); | |
| 149 } | |
| 150 | |
| 151 public override IsExpressionResult IsExpression(Expression expression, int level, RequestFor requestFlag) | |
| 152 { | |
| 153 switch (requestFlag) | |
| 154 { | |
| 155 case RequestFor.Root : return new IsExpressionResult(Lambda != null && expression == Lambda.Parameters[0]); | |
| 156 case RequestFor.Expression : return IsExpressionResult.True; | |
| 157 } | |
| 158 | |
| 159 return IsExpressionResult.False; | |
| 160 } | |
| 161 | |
| 162 public override IBuildContext GetContext(Expression expression, int level, BuildInfo buildInfo) | |
| 163 { | |
| 164 throw new InvalidOperationException(); | |
| 165 } | |
| 166 } | |
| 167 } | |
| 168 } |
