0
|
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 }
|