comparison Source/Data/Linq/Builder/ExpressionBuilder.QueryBuilder.cs @ 0:f990fcb411a9

Копия текущей версии из github
author cin
date Thu, 27 Mar 2014 21:46:09 +0400
parents
children 1ef98bd70424
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 using System.Reflection;
8
9 namespace BLToolkit.Data.Linq.Builder
10 {
11 using BLToolkit.Linq;
12 using Reflection;
13
14 partial class ExpressionBuilder
15 {
16 #region BuildExpression
17
18 readonly HashSet<Expression> _skippedExpressions = new HashSet<Expression>();
19
20 public Expression BuildExpression(IBuildContext context, Expression expression)
21 {
22 var newExpr = expression.Convert2(expr =>
23 {
24 if (_skippedExpressions.Contains(expr))
25 return new ExpressionHelper.ConvertInfo(expr, true);
26
27 if (expr.Find(IsNoneSqlMember) != null)
28 return new ExpressionHelper.ConvertInfo(expr);
29
30 switch (expr.NodeType)
31 {
32 case ExpressionType.MemberAccess:
33 {
34 if (IsServerSideOnly(expr) || PreferServerSide(expr))
35 return new ExpressionHelper.ConvertInfo(BuildSql(context, expr));
36
37 var ma = (MemberExpression)expr;
38
39 if (SqlProvider.ConvertMember(ma.Member) != null)
40 break;
41
42 var ctx = GetContext(context, expr);
43
44 if (ctx != null)
45 return new ExpressionHelper.ConvertInfo(ctx.BuildExpression(expr, 0));
46
47 var ex = ma.Expression;
48
49 if (ex != null && ex.NodeType == ExpressionType.Constant)
50 {
51 // field = localVariable
52 //
53 var c = _expressionAccessors[ex];
54 return new ExpressionHelper.ConvertInfo(
55 Expression.MakeMemberAccess(Expression.Convert(c, ex.Type), ma.Member));
56 }
57
58 break;
59 }
60
61 case ExpressionType.Parameter:
62 {
63 if (expr == ParametersParam)
64 break;
65
66 var ctx = GetContext(context, expr);
67
68 if (ctx != null)
69 return new ExpressionHelper.ConvertInfo(ctx.BuildExpression(expr, 0));
70
71 break;
72 }
73
74 case ExpressionType.Constant:
75 {
76 if (ExpressionHelper.IsConstant(expr.Type))
77 break;
78
79 if (_expressionAccessors.ContainsKey(expr))
80 return new ExpressionHelper.ConvertInfo(Expression.Convert(_expressionAccessors[expr], expr.Type));
81
82 break;
83 }
84
85 case ExpressionType.Coalesce:
86
87 if (expr.Type == typeof(string) && MappingSchema.GetDefaultNullValue<string>() != null)
88 return new ExpressionHelper.ConvertInfo(BuildSql(context, expr));
89
90 if (CanBeTranslatedToSql(context, ConvertExpression(expr), true))
91 return new ExpressionHelper.ConvertInfo(BuildSql(context, expr));
92
93 break;
94
95 case ExpressionType.Conditional:
96
97 if (CanBeTranslatedToSql(context, ConvertExpression(expr), true))
98 return new ExpressionHelper.ConvertInfo(BuildSql(context, expr));
99 break;
100
101 case ExpressionType.Call:
102 {
103 var ce = (MethodCallExpression)expr;
104
105 if (IsGroupJoinSource(context, ce))
106 {
107 foreach (var arg in ce.Arguments.Skip(1))
108 if (!_skippedExpressions.Contains(arg))
109 _skippedExpressions.Add(arg);
110
111 break;
112 }
113
114 if (IsSubQuery(context, ce))
115 {
116 if (TypeHelper.IsSameOrParent(typeof(IEnumerable), expr.Type) && expr.Type != typeof(string) && !expr.Type.IsArray)
117 return new ExpressionHelper.ConvertInfo(BuildMultipleQuery(context, expr));
118
119 return new ExpressionHelper.ConvertInfo(GetSubQuery(context, ce).BuildExpression(null, 0));
120 }
121
122 if (IsServerSideOnly(expr) || PreferServerSide(expr))
123 return new ExpressionHelper.ConvertInfo(BuildSql(context, expr));
124 }
125
126 break;
127 }
128
129 if (EnforceServerSide(context))
130 {
131 switch (expr.NodeType)
132 {
133 case ExpressionType.MemberInit :
134 case ExpressionType.New :
135 case ExpressionType.Convert :
136 break;
137
138 default :
139 if (CanBeCompiled(expr))
140 break;
141 return new ExpressionHelper.ConvertInfo(BuildSql(context, expr));
142 }
143 }
144
145 return new ExpressionHelper.ConvertInfo(expr);
146 });
147
148 return newExpr;
149 }
150
151 static bool EnforceServerSide(IBuildContext context)
152 {
153 return context.SqlQuery.Select.IsDistinct;
154 }
155
156 #endregion
157
158 #region BuildSql
159
160 Expression BuildSql(IBuildContext context, Expression expression)
161 {
162 var sqlex = ConvertToSqlExpression(context, expression, true);
163 var idx = context.SqlQuery.Select.Add(sqlex);
164
165 idx = context.ConvertToParentIndex(idx, context);
166
167 var field = BuildSql(expression.Type, idx);
168
169 return field;
170 }
171
172 public Expression BuildSql(MemberAccessor ma, int idx, MethodInfo checkNullFunction, Expression context)
173 {
174 var expr = Expression.Call(DataReaderParam, ReflectionHelper.DataReader.GetValue, Expression.Constant(idx));
175
176 if (checkNullFunction != null)
177 expr = Expression.Call(null, checkNullFunction, expr, context);
178
179 Expression mapper;
180
181 if (TypeHelper.IsEnumOrNullableEnum(ma.Type))
182 {
183 var type = TypeHelper.ToNullable(ma.Type);
184 mapper =
185 Expression.Convert(
186 Expression.Call(
187 Expression.Constant(MappingSchema),
188 ReflectionHelper.MapSchema.MapValueToEnumWithMemberAccessor,
189 expr,
190 Expression.Constant(ma)),
191 type);
192 }
193 else
194 {
195 MethodInfo mi;
196
197 if (!ReflectionHelper.MapSchema.Converters.TryGetValue(ma.Type, out mi))
198 {
199 mapper =
200 Expression.Convert(
201 Expression.Call(
202 Expression.Constant(MappingSchema),
203 ReflectionHelper.MapSchema.ChangeType,
204 expr,
205 Expression.Constant(ma.Type)),
206 ma.Type);
207 }
208 else
209 {
210 mapper = Expression.Call(Expression.Constant(MappingSchema), mi, expr);
211 }
212 }
213
214 return mapper;
215 }
216
217 public Expression BuildSql(Type type, int idx, MethodInfo checkNullFunction, Expression context)
218 {
219 var expr = Expression.Call(DataReaderParam, ReflectionHelper.DataReader.GetValue, Expression.Constant(idx));
220
221 if (checkNullFunction != null)
222 expr = Expression.Call(null, checkNullFunction, expr, context);
223
224 Expression mapper;
225
226 if (type.IsEnum)
227 {
228 mapper =
229 Expression.Convert(
230 Expression.Call(
231 Expression.Constant(MappingSchema),
232 ReflectionHelper.MapSchema.MapValueToEnum,
233 expr,
234 Expression.Constant(type)),
235 type);
236 }
237 else
238 {
239 MethodInfo mi;
240
241 if (!ReflectionHelper.MapSchema.Converters.TryGetValue(type, out mi))
242 {
243 mapper =
244 Expression.Convert(
245 Expression.Call(
246 Expression.Constant(MappingSchema),
247 ReflectionHelper.MapSchema.ChangeType,
248 expr,
249 Expression.Constant(type)),
250 type);
251 }
252 else
253 {
254 mapper = Expression.Call(Expression.Constant(MappingSchema), mi, expr);
255 }
256 }
257
258 return mapper;
259 }
260
261 public Expression BuildSql(Type type, int idx)
262 {
263 return BuildSql(type, idx, null, null);
264 }
265
266 public Expression BuildSql(MemberAccessor ma, int idx)
267 {
268 return BuildSql(ma, idx, null, null);
269 }
270
271 #endregion
272
273 #region IsNonSqlMember
274
275 bool IsNoneSqlMember(Expression expr)
276 {
277 switch (expr.NodeType)
278 {
279 case ExpressionType.MemberAccess:
280 {
281 var me = (MemberExpression)expr;
282
283 var om = (
284 from c in Contexts.OfType<TableBuilder.TableContext>()
285 where c.ObjectType == me.Member.DeclaringType
286 select c.ObjectMapper
287 ).FirstOrDefault();
288
289 return om != null && om.Associations.All(a => !TypeHelper.Equals(a.MemberAccessor.MemberInfo, me.Member)) && om[me.Member.Name, true] == null;
290 }
291 }
292
293 return false;
294 }
295
296 #endregion
297
298 #region PreferServerSide
299
300 bool PreferServerSide(Expression expr)
301 {
302 switch (expr.NodeType)
303 {
304 case ExpressionType.MemberAccess:
305 {
306 var pi = (MemberExpression)expr;
307 var l = SqlProvider.ConvertMember(pi.Member);
308
309 if (l != null)
310 {
311 var info = l.Body.Unwrap();
312
313 if (l.Parameters.Count == 1 && pi.Expression != null)
314 info = info.Convert(wpi => wpi == l.Parameters[0] ? pi.Expression : wpi);
315
316 return info.Find(PreferServerSide) != null;
317 }
318
319 var attr = GetFunctionAttribute(pi.Member);
320 return attr != null && attr.PreferServerSide && !CanBeCompiled(expr);
321 }
322
323 case ExpressionType.Call:
324 {
325 var pi = (MethodCallExpression)expr;
326 var e = pi;
327 var l = SqlProvider.ConvertMember(e.Method);
328
329 if (l != null)
330 return l.Body.Unwrap().Find(PreferServerSide) != null;
331
332 var attr = GetFunctionAttribute(e.Method);
333 return attr != null && attr.PreferServerSide && !CanBeCompiled(expr);
334 }
335 }
336
337 return false;
338 }
339
340 #endregion
341
342 #region Build Mapper
343
344 public Expression BuildBlock(Expression expression)
345 {
346 #if FW4 || SILVERLIGHT
347
348 if (IsBlockDisable || BlockExpressions.Count == 0)
349 return expression;
350
351 BlockExpressions.Add(expression);
352
353 expression = Expression.Block(BlockVariables, BlockExpressions);
354
355 BlockVariables. Clear();
356 BlockExpressions.Clear();
357
358 #endif
359
360 return expression;
361 }
362
363 public Expression<Func<QueryContext,IDataContext,IDataReader,Expression,object[],T>> BuildMapper<T>(Expression expr)
364 {
365 var type = typeof(T);
366
367 if (expr.Type != type)
368 expr = Expression.Convert(expr, type);
369
370 var mapper = Expression.Lambda<Func<QueryContext,IDataContext,IDataReader,Expression,object[],T>>(
371 BuildBlock(expr), new []
372 {
373 ContextParam,
374 DataContextParam,
375 DataReaderParam,
376 ExpressionParam,
377 ParametersParam,
378 });
379
380 return mapper;
381 }
382
383 #endregion
384
385 #region BuildMultipleQuery
386
387 interface IMultipleQueryHelper
388 {
389 Expression GetSubquery(
390 ExpressionBuilder builder,
391 Expression expression,
392 ParameterExpression paramArray,
393 IEnumerable<Expression> parameters);
394 }
395
396 class MultipleQueryHelper<TRet> : IMultipleQueryHelper
397 {
398 public Expression GetSubquery(
399 ExpressionBuilder builder,
400 Expression expression,
401 ParameterExpression paramArray,
402 IEnumerable<Expression> parameters)
403 {
404 var lambda = Expression.Lambda<Func<IDataContext,object[],TRet>>(
405 expression,
406 Expression.Parameter(typeof(IDataContext), "ctx"),
407 paramArray);
408 var queryReader = CompiledQuery.Compile(lambda);
409
410 return Expression.Call(
411 null,
412 ReflectionHelper.Expressor<object>.MethodExpressor(_ => ExecuteSubQuery(null, null, null)),
413 ContextParam,
414 Expression.NewArrayInit(typeof(object), parameters),
415 Expression.Constant(queryReader)
416 );
417 }
418
419 static TRet ExecuteSubQuery(
420 QueryContext queryContext,
421 object[] parameters,
422 Func<IDataContext,object[],TRet> queryReader)
423 {
424 var db = queryContext.GetDataContext();
425
426 try
427 {
428 return queryReader(db.DataContextInfo.DataContext, parameters);
429 }
430 finally
431 {
432 queryContext.ReleaseDataContext(db);
433 }
434 }
435 }
436
437 public Expression BuildMultipleQuery(IBuildContext context, Expression expression)
438 {
439 if (!Common.Configuration.Linq.AllowMultipleQuery)
440 throw new LinqException("Multiple queries are not allowed. Set the 'BLToolkit.Common.Configuration.Linq.AllowMultipleQuery' flag to 'true' to allow multiple queries.");
441
442 var parameters = new HashSet<ParameterExpression>();
443
444 expression.Visit(e =>
445 {
446 if (e.NodeType == ExpressionType.Lambda)
447 foreach (var p in ((LambdaExpression)e).Parameters)
448 parameters.Add(p);
449 });
450
451 // Convert associations.
452 //
453 expression = expression.Convert(e =>
454 {
455 switch (e.NodeType)
456 {
457 case ExpressionType.MemberAccess :
458 {
459 var root = e.GetRootObject();
460
461 if (root != null &&
462 root.NodeType == ExpressionType.Parameter &&
463 !parameters.Contains((ParameterExpression)root))
464 {
465 var res = context.IsExpression(e, 0, RequestFor.Association);
466
467 if (res.Result)
468 {
469 var table = (TableBuilder.AssociatedTableContext)res.Context;
470
471 if (table.IsList)
472 {
473 var ttype = typeof(Table<>).MakeGenericType(table.ObjectType);
474 var tbl = Activator.CreateInstance(ttype);
475 var method = typeof(LinqExtensions)
476 .GetMethod("Where", BindingFlags.NonPublic | BindingFlags.Static)
477 .MakeGenericMethod(e.Type, table.ObjectType, ttype);
478
479 var me = (MemberExpression)e;
480 var op = Expression.Parameter(table.ObjectType, "t");
481
482 parameters.Add(op);
483
484 Expression ex = null;
485
486 for (var i = 0; i < table.Association.ThisKey.Length; i++)
487 {
488 var field1 = table.ParentAssociation.SqlTable.Fields[table.Association.ThisKey [i]];
489 var field2 = table. SqlTable.Fields[table.Association.OtherKey[i]];
490
491 var ee = Expression.Equal(
492 Expression.MakeMemberAccess(op, field2.MemberMapper.MemberAccessor.MemberInfo),
493 Expression.MakeMemberAccess(me.Expression, field1.MemberMapper.MemberAccessor.MemberInfo));
494
495 ex = ex == null ? ee : Expression.AndAlso(ex, ee);
496 }
497
498 return Expression.Call(null, method, Expression.Constant(tbl), Expression.Lambda(ex, op));
499 }
500 }
501 }
502
503 break;
504 }
505 }
506
507 return e;
508 });
509
510 var paramex = Expression.Parameter(typeof(object[]), "ps");
511 var parms = new List<Expression>();
512
513 // Convert parameters.
514 //
515 expression = expression.Convert(e =>
516 {
517 var root = e.GetRootObject();
518
519 if (root != null &&
520 root.NodeType == ExpressionType.Parameter &&
521 !parameters.Contains((ParameterExpression)root))
522 {
523 var ex = Expression.Convert(BuildExpression(context, e), typeof(object));
524
525 parms.Add(ex);
526
527 return Expression.Convert(
528 Expression.ArrayIndex(paramex, Expression.Constant(parms.Count - 1)),
529 e.Type);
530 }
531
532 return e;
533 });
534
535 var sqtype = typeof(MultipleQueryHelper<>).MakeGenericType(expression.Type);
536 var helper = (IMultipleQueryHelper)Activator.CreateInstance(sqtype);
537
538 return helper.GetSubquery(this, expression, paramex, parms);
539 }
540
541 #endregion
542 }
543 }