Mercurial > pub > bltoolkit
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 } |