comparison Source/Data/Linq/Builder/SelectBuilder.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.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 }