0
|
1 using System;
|
|
2 using System.Collections;
|
|
3 using System.Collections.Generic;
|
|
4 using System.Data;
|
|
5 using System.Linq;
|
|
6 using System.Linq.Expressions;
|
|
7
|
|
8 namespace BLToolkit.Data.Linq.Builder
|
|
9 {
|
|
10 using BLToolkit.Linq;
|
|
11 using Reflection;
|
|
12
|
|
13 class SelectBuilder : MethodCallBuilder
|
|
14 {
|
|
15 #region SelectBuilder
|
|
16
|
|
17 protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
|
|
18 {
|
|
19 if (methodCall.IsQueryable("Select"))
|
|
20 {
|
|
21 switch (((LambdaExpression)methodCall.Arguments[1].Unwrap()).Parameters.Count)
|
|
22 {
|
|
23 case 1 :
|
|
24 case 2 : return true;
|
|
25 default: break;
|
|
26 }
|
|
27 }
|
|
28
|
|
29 return false;
|
|
30 }
|
|
31
|
|
32 protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
|
|
33 {
|
|
34 var selector = (LambdaExpression)methodCall.Arguments[1].Unwrap();
|
|
35 var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));
|
|
36
|
|
37 sequence.SetAlias(selector.Parameters[0].Name);
|
|
38
|
|
39 var body = selector.Body.Unwrap();
|
|
40
|
|
41 switch (body.NodeType)
|
|
42 {
|
|
43 case ExpressionType.Parameter : break;
|
|
44 default :
|
|
45 sequence = CheckSubQueryForSelect(sequence);
|
|
46 break;
|
|
47 }
|
|
48
|
|
49 var context = selector.Parameters.Count == 1 ?
|
|
50 new SelectContext (buildInfo.Parent, selector, sequence) :
|
|
51 new SelectContext2(buildInfo.Parent, selector, sequence);
|
|
52
|
|
53 #if DEBUG
|
|
54 context.MethodCall = methodCall;
|
|
55 #endif
|
|
56
|
|
57 return context;
|
|
58 }
|
|
59
|
|
60 static IBuildContext CheckSubQueryForSelect(IBuildContext context)
|
|
61 {
|
|
62 return context.SqlQuery.Select.IsDistinct ? new SubQueryContext(context) : context;
|
|
63 }
|
|
64
|
|
65 #endregion
|
|
66
|
|
67 #region SelectContext2
|
|
68
|
|
69 class SelectContext2 : SelectContext
|
|
70 {
|
|
71 public SelectContext2(IBuildContext parent, LambdaExpression lambda, IBuildContext sequence)
|
|
72 : base(parent, lambda, sequence)
|
|
73 {
|
|
74 }
|
|
75
|
|
76 static readonly ParameterExpression _counterParam = Expression.Parameter(typeof(int), "counter");
|
|
77
|
|
78 public override void BuildQuery<T>(Query<T> query, ParameterExpression queryParameter)
|
|
79 {
|
|
80 var expr = BuildExpression(null, 0);
|
|
81
|
|
82 if (expr.Type != typeof(T))
|
|
83 expr = Expression.Convert(expr, typeof(T));
|
|
84
|
|
85 var mapper = Expression.Lambda<Func<int,QueryContext,IDataContext,IDataReader,Expression,object[],T>>(
|
|
86 Builder.BuildBlock(expr), new []
|
|
87 {
|
|
88 _counterParam,
|
|
89 ExpressionBuilder.ContextParam,
|
|
90 ExpressionBuilder.DataContextParam,
|
|
91 ExpressionBuilder.DataReaderParam,
|
|
92 ExpressionBuilder.ExpressionParam,
|
|
93 ExpressionBuilder.ParametersParam,
|
|
94 });
|
|
95
|
|
96 var func = mapper.Compile();
|
|
97
|
|
98 Func<QueryContext,IDataContext,IDataReader,Expression,object[],int,T> map = (ctx,db,rd,e,ps,n) => func(n, ctx, db, rd, e, ps);
|
|
99
|
|
100 query.SetQuery(map);
|
|
101 }
|
|
102
|
|
103 public override IsExpressionResult IsExpression(Expression expression, int level, RequestFor requestFlag)
|
|
104 {
|
|
105 switch (requestFlag)
|
|
106 {
|
|
107 case RequestFor.Expression :
|
|
108 case RequestFor.Root :
|
|
109 if (expression == Lambda.Parameters[1])
|
|
110 return IsExpressionResult.True;
|
|
111 break;
|
|
112 }
|
|
113
|
|
114 return base.IsExpression(expression, level, requestFlag);
|
|
115 }
|
|
116
|
|
117 public override Expression BuildExpression(Expression expression, int level)
|
|
118 {
|
|
119 if (expression == Lambda.Parameters[1])
|
|
120 return _counterParam;
|
|
121
|
|
122 return base.BuildExpression(expression, level);
|
|
123 }
|
|
124 }
|
|
125
|
|
126 #endregion
|
|
127
|
|
128 #region Convert
|
|
129
|
|
130 protected override SequenceConvertInfo Convert(
|
|
131 ExpressionBuilder builder, MethodCallExpression originalMethodCall, BuildInfo buildInfo, ParameterExpression param)
|
|
132 {
|
|
133 var methodCall = originalMethodCall;
|
|
134 var selector = (LambdaExpression)methodCall.Arguments[1].Unwrap();
|
|
135 var info = builder.ConvertSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]), selector.Parameters[0]);
|
|
136
|
|
137 if (info != null)
|
|
138 {
|
|
139 methodCall = (MethodCallExpression)methodCall.Convert(
|
|
140 ex => ConvertMethod(methodCall, 0, info, selector.Parameters[0], ex));
|
|
141 selector = (LambdaExpression)methodCall.Arguments[1].Unwrap();
|
|
142 }
|
|
143
|
|
144 if (param != null && param != builder.SequenceParameter)
|
|
145 {
|
|
146 var list =
|
|
147 (
|
|
148 from path in GetExpressions(selector.Parameters[0], param, 0, selector.Body.Unwrap())
|
|
149 orderby path.Level descending
|
|
150 select path
|
|
151 ).ToList();
|
|
152
|
|
153 if (list.Count > 0)
|
|
154 {
|
|
155 var plist = list.Where(e => e.Expr == selector.Parameters[0]).ToList();
|
|
156
|
|
157 if (plist.Count > 1)
|
|
158 list = list.Except(plist.Skip(1)).ToList();
|
|
159
|
|
160 var p = plist.FirstOrDefault();
|
|
161
|
|
162 if (p == null)
|
|
163 {
|
|
164 var types = methodCall.Method.GetGenericArguments();
|
|
165 var mgen = methodCall.Method.GetGenericMethodDefinition();
|
|
166 var btype = typeof(ExpressionHoder<,>).MakeGenericType(types[0], selector.Body.Type);
|
|
167 var fields = btype.GetFields();
|
|
168 var pold = selector.Parameters[0];
|
|
169 var psel = Expression.Parameter(types[0], pold.Name);
|
|
170
|
|
171 methodCall = Expression.Call(
|
|
172 methodCall.Object,
|
|
173 mgen.MakeGenericMethod(types[0], btype),
|
|
174 methodCall.Arguments[0],
|
|
175 Expression.Lambda(
|
|
176 Expression.MemberInit(
|
|
177 Expression.New(btype),
|
|
178 Expression.Bind(fields[0], psel),
|
|
179 Expression.Bind(fields[1], selector.Body.Convert(e => e == pold ? psel : e))),
|
|
180 psel));
|
|
181
|
|
182 selector = (LambdaExpression)methodCall.Arguments[1].Unwrap();
|
|
183 param = Expression.Parameter(selector.Body.Type, param.Name);
|
|
184
|
|
185 list.Add(new SequenceConvertPath { Path = param, Expr = Expression.MakeMemberAccess(param, fields[1]), Level = 1 });
|
|
186
|
|
187 var expr = Expression.MakeMemberAccess(param, fields[0]);
|
|
188
|
|
189 foreach (var t in list)
|
|
190 t.Expr = t.Expr.Convert(ex => ex == pold ? expr : ex);
|
|
191
|
|
192 return new SequenceConvertInfo
|
|
193 {
|
|
194 Parameter = param,
|
|
195 Expression = methodCall,
|
|
196 ExpressionsToReplace = list
|
|
197 };
|
|
198 }
|
|
199
|
|
200 if (info != null)
|
|
201 {
|
|
202 if (info.ExpressionsToReplace != null)
|
|
203 {
|
|
204 foreach (var path in info.ExpressionsToReplace)
|
|
205 {
|
|
206 path.Path = path.Path.Convert(e => e == info.Parameter ? p.Path : e);
|
|
207 path.Expr = path.Expr.Convert(e => e == info.Parameter ? p.Path : e);
|
|
208 path.Level += p.Level;
|
|
209
|
|
210 list.Add(path);
|
|
211 }
|
|
212
|
|
213 list = list.OrderByDescending(path => path.Level).ToList();
|
|
214 }
|
|
215 }
|
|
216
|
|
217 if (list.Count > 1)
|
|
218 {
|
|
219 return new SequenceConvertInfo
|
|
220 {
|
|
221 Parameter = param,
|
|
222 Expression = methodCall,
|
|
223 ExpressionsToReplace = list
|
|
224 .Where (e => e != p)
|
|
225 .Select(ei =>
|
|
226 {
|
|
227 ei.Expr = ei.Expr.Convert(e => e == p.Expr ? p.Path : e);
|
|
228 return ei;
|
|
229 })
|
|
230 .ToList()
|
|
231 };
|
|
232 }
|
|
233 }
|
|
234 }
|
|
235
|
|
236 if (methodCall != originalMethodCall)
|
|
237 return new SequenceConvertInfo
|
|
238 {
|
|
239 Parameter = param,
|
|
240 Expression = methodCall,
|
|
241 };
|
|
242
|
|
243 return null;
|
|
244 }
|
|
245
|
|
246 static IEnumerable<SequenceConvertPath> GetExpressions(ParameterExpression param, Expression path, int level, Expression expression)
|
|
247 {
|
|
248 switch (expression.NodeType)
|
|
249 {
|
|
250 // new { ... }
|
|
251 //
|
|
252 case ExpressionType.New :
|
|
253 {
|
|
254 var expr = (NewExpression)expression;
|
|
255
|
|
256 if (expr.Members != null) for (var i = 0; i < expr.Members.Count; i++)
|
|
257 {
|
|
258 var q = GetExpressions(param, Expression.MakeMemberAccess(path, expr.Members[i]), level + 1, expr.Arguments[i]);
|
|
259 foreach (var e in q)
|
|
260 yield return e;
|
|
261 }
|
|
262
|
|
263 break;
|
|
264 }
|
|
265
|
|
266 // new MyObject { ... }
|
|
267 //
|
|
268 case ExpressionType.MemberInit :
|
|
269 {
|
|
270 var expr = (MemberInitExpression)expression;
|
|
271 var dic = TypeAccessor.GetAccessor(expr.Type)
|
|
272 .Select((m,i) => new { m, i })
|
|
273 .ToDictionary(_ => _.m.MemberInfo.Name, _ => _.i);
|
|
274
|
|
275 foreach (var binding in expr.Bindings.Cast<MemberAssignment>().OrderBy(b => dic[b.Member.Name]))
|
|
276 {
|
|
277 var q = GetExpressions(param, Expression.MakeMemberAccess(path, binding.Member), level + 1, binding.Expression);
|
|
278 foreach (var e in q)
|
|
279 yield return e;
|
|
280 }
|
|
281
|
|
282 break;
|
|
283 }
|
|
284
|
|
285 // parameter
|
|
286 //
|
|
287 case ExpressionType.Parameter :
|
|
288 if (expression == param)
|
|
289 yield return new SequenceConvertPath { Path = path, Expr = expression, Level = level };
|
|
290 break;
|
|
291
|
|
292 case ExpressionType.TypeAs :
|
|
293 yield return new SequenceConvertPath { Path = path, Expr = expression, Level = level };
|
|
294 break;
|
|
295
|
|
296 // Queriable method.
|
|
297 //
|
|
298 case ExpressionType.Call :
|
|
299 {
|
|
300 var call = (MethodCallExpression)expression;
|
|
301
|
|
302 if (call.IsQueryable())
|
|
303 if (TypeHelper.IsSameOrParent(typeof(IEnumerable), call.Type) ||
|
|
304 TypeHelper.IsSameOrParent(typeof(IQueryable), call.Type) ||
|
|
305 FirstSingleBuilder.MethodNames.Contains(call.Method.Name))
|
|
306 yield return new SequenceConvertPath { Path = path, Expr = expression, Level = level };
|
|
307
|
|
308 break;
|
|
309 }
|
|
310 }
|
|
311 }
|
|
312
|
|
313 #endregion
|
|
314 }
|
|
315 }
|