comparison Source/Data/Linq/Builder/FirstSingleBuilder.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 FirstSingleBuilder : MethodCallBuilder
12 {
13 public static string[] MethodNames = new[] { "First", "FirstOrDefault", "Single", "SingleOrDefault" };
14
15 protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
16 {
17 return
18 methodCall.IsQueryable(MethodNames) &&
19 methodCall.Arguments.Count == 1;
20 }
21
22 protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
23 {
24 var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));
25 var take = 0;
26
27 if (!buildInfo.IsSubQuery || builder.SqlProvider.IsSubQueryTakeSupported)
28 switch (methodCall.Method.Name)
29 {
30 case "First" :
31 case "FirstOrDefault" :
32 take = 1;
33 break;
34
35 case "Single" :
36 case "SingleOrDefault" :
37 if (!buildInfo.IsSubQuery)
38 take = 2;
39 break;
40 }
41
42 if (take != 0)
43 builder.BuildTake(sequence, new SqlValue(take));
44
45 return new FirstSingleContext(buildInfo.Parent, sequence, methodCall);
46 }
47
48 protected override SequenceConvertInfo Convert(
49 ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo, ParameterExpression param)
50 {
51 if (methodCall.Arguments.Count == 2)
52 {
53 var predicate = (LambdaExpression)methodCall.Arguments[1].Unwrap();
54 var info = builder.ConvertSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]), predicate.Parameters[0]);
55
56 if (info != null)
57 {
58 info.Expression = methodCall.Convert(ex => ConvertMethod(methodCall, 0, info, predicate.Parameters[0], ex));
59 info.Parameter = param;
60
61 return info;
62 }
63 }
64 else
65 {
66 var info = builder.ConvertSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]), null);
67
68 if (info != null)
69 {
70 info.Expression = methodCall.Convert(ex => ConvertMethod(methodCall, 0, info, null, ex));
71 info.Parameter = param;
72
73 return info;
74 }
75 }
76
77 return null;
78 }
79
80 public class FirstSingleContext : SequenceContextBase
81 {
82 public FirstSingleContext(IBuildContext parent, IBuildContext sequence, MethodCallExpression methodCall)
83 : base(parent, sequence, null)
84 {
85 _methodCall = methodCall;
86 }
87
88 readonly MethodCallExpression _methodCall;
89
90 public override void BuildQuery<T>(Query<T> query, ParameterExpression queryParameter)
91 {
92 Sequence.BuildQuery(query, queryParameter);
93
94 switch (_methodCall.Method.Name)
95 {
96 case "First" : query.GetElement = (ctx, db, expr, ps) => query.GetIEnumerable(ctx, db, expr, ps).First(); break;
97 case "FirstOrDefault" : query.GetElement = (ctx, db, expr, ps) => query.GetIEnumerable(ctx, db, expr, ps).FirstOrDefault(); break;
98 case "Single" : query.GetElement = (ctx, db, expr, ps) => query.GetIEnumerable(ctx, db, expr, ps).Single(); break;
99 case "SingleOrDefault" : query.GetElement = (ctx, db, expr, ps) => query.GetIEnumerable(ctx, db, expr, ps).SingleOrDefault(); break;
100 }
101 }
102
103 static object SequenceException()
104 {
105 return new object[0].First();
106 }
107
108 public override Expression BuildExpression(Expression expression, int level)
109 {
110 if (expression == null)
111 {
112 if (Builder.SqlProvider.IsApplyJoinSupported && Parent.SqlQuery.GroupBy.IsEmpty)
113 {
114 var join = SqlQuery.OuterApply(SqlQuery);
115
116 Parent.SqlQuery.From.Tables[0].Joins.Add(join.JoinedTable);
117
118 var expr = Sequence.BuildExpression(expression, level);
119 var idx = SqlQuery.Select.Add(new SqlValue(1));
120
121 idx = ConvertToParentIndex(idx, this);
122
123 var defaultValue = _methodCall.Method.Name.EndsWith("OrDefault") ?
124 Expression.Constant(TypeHelper.GetDefaultValue(expr.Type), expr.Type) as Expression :
125 Expression.Convert(
126 Expression.Call(
127 null,
128 ReflectionHelper.Expressor<object>.MethodExpressor(_ => SequenceException())),
129 expr.Type);
130
131 expr = Expression.Condition(
132 Expression.Call(
133 ExpressionBuilder.DataReaderParam,
134 ReflectionHelper.DataReader.IsDBNull,
135 Expression.Constant(idx)),
136 defaultValue,
137 expr);
138
139 return expr;
140 }
141
142 if (Sequence.IsExpression(null, level, RequestFor.Object).Result)
143 return Builder.BuildMultipleQuery(Parent, _methodCall);
144
145 return Builder.BuildSql(_methodCall.Type, Parent.SqlQuery.Select.Add(SqlQuery));
146 }
147
148 throw new InvalidOperationException();
149 }
150
151 public override SqlInfo[] ConvertToSql(Expression expression, int level, ConvertFlags flags)
152 {
153 return Sequence.ConvertToSql(expression, level + 1, flags);
154 }
155
156 public override SqlInfo[] ConvertToIndex(Expression expression, int level, ConvertFlags flags)
157 {
158 return Sequence.ConvertToIndex(expression, level, flags);
159 }
160
161 public override IsExpressionResult IsExpression(Expression expression, int level, RequestFor requestFlag)
162 {
163 return Sequence.IsExpression(expression, level, requestFlag);
164 }
165
166 public override IBuildContext GetContext(Expression expression, int level, BuildInfo buildInfo)
167 {
168 throw new InvalidOperationException();
169 }
170 }
171 }
172 }