Mercurial > pub > bltoolkit
comparison Source/Data/Linq/Builder/TableBuilder.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children | 8f65451dc28f |
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 Data.Sql; | |
13 using Mapping; | |
14 using Reflection; | |
15 using Reflection.Extension; | |
16 | |
17 class TableBuilder : ISequenceBuilder | |
18 { | |
19 #region TableBuilder | |
20 | |
21 int ISequenceBuilder.BuildCounter { get; set; } | |
22 | |
23 static T Find<T>(ExpressionBuilder builder, BuildInfo buildInfo, Func<int,IBuildContext,T> action) | |
24 { | |
25 var expression = buildInfo.Expression; | |
26 | |
27 switch (expression.NodeType) | |
28 { | |
29 case ExpressionType.Constant: | |
30 { | |
31 var c = (ConstantExpression)expression; | |
32 if (c.Value is IQueryable) | |
33 return action(1, null); | |
34 | |
35 break; | |
36 } | |
37 | |
38 case ExpressionType.Call: | |
39 { | |
40 var mc = (MethodCallExpression)expression; | |
41 | |
42 if (mc.Method.Name == "GetTable") | |
43 if (expression.Type.IsGenericType && expression.Type.GetGenericTypeDefinition() == typeof(Table<>)) | |
44 return action(2, null); | |
45 | |
46 var attr = builder.GetTableFunctionAttribute(mc.Method); | |
47 | |
48 if (attr != null) | |
49 return action(5, null); | |
50 | |
51 break; | |
52 } | |
53 | |
54 case ExpressionType.MemberAccess: | |
55 | |
56 if (expression.Type.IsGenericType && expression.Type.GetGenericTypeDefinition() == typeof(Table<>)) | |
57 return action(3, null); | |
58 | |
59 // Looking for association. | |
60 // | |
61 if (buildInfo.IsSubQuery && buildInfo.SqlQuery.From.Tables.Count == 0) | |
62 { | |
63 var ctx = builder.GetContext(buildInfo.Parent, expression); | |
64 if (ctx != null) | |
65 return action(4, ctx); | |
66 } | |
67 | |
68 break; | |
69 | |
70 case ExpressionType.Parameter: | |
71 { | |
72 if (buildInfo.IsSubQuery && buildInfo.SqlQuery.From.Tables.Count == 0) | |
73 { | |
74 var ctx = builder.GetContext(buildInfo.Parent, expression); | |
75 if (ctx != null) | |
76 return action(4, ctx); | |
77 } | |
78 | |
79 break; | |
80 } | |
81 } | |
82 | |
83 return action(0, null); | |
84 } | |
85 | |
86 public bool CanBuild(ExpressionBuilder builder, BuildInfo buildInfo) | |
87 { | |
88 return Find(builder, buildInfo, (n,_) => n > 0); | |
89 } | |
90 | |
91 public IBuildContext BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo) | |
92 { | |
93 return Find(builder, buildInfo, (n,ctx) => | |
94 { | |
95 switch (n) | |
96 { | |
97 case 0 : return null; | |
98 case 1 : return new TableContext(builder, buildInfo, ((IQueryable)((ConstantExpression)buildInfo.Expression).Value).ElementType); | |
99 case 2 : | |
100 case 3 : return new TableContext(builder, buildInfo, buildInfo.Expression.Type.GetGenericArguments()[0]); | |
101 case 4 : return ctx.GetContext(buildInfo.Expression, 0, buildInfo); | |
102 case 5 : return new TableContext(builder, buildInfo); | |
103 } | |
104 | |
105 throw new InvalidOperationException(); | |
106 }); | |
107 } | |
108 | |
109 public SequenceConvertInfo Convert(ExpressionBuilder builder, BuildInfo buildInfo, ParameterExpression param) | |
110 { | |
111 return null; | |
112 } | |
113 | |
114 public bool IsSequence(ExpressionBuilder builder, BuildInfo buildInfo) | |
115 { | |
116 return true; | |
117 } | |
118 | |
119 #endregion | |
120 | |
121 #region TableContext | |
122 | |
123 public class TableContext : IBuildContext | |
124 { | |
125 #region Properties | |
126 | |
127 #if DEBUG | |
128 public string _sqlQueryText { get { return SqlQuery == null ? "" : SqlQuery.SqlText; } } | |
129 #endif | |
130 | |
131 public ExpressionBuilder Builder { get; private set; } | |
132 public Expression Expression { get; private set; } | |
133 public SqlQuery SqlQuery { get; set; } | |
134 | |
135 public virtual IBuildContext Parent { get; set; } | |
136 | |
137 public Type OriginalType; | |
138 public Type ObjectType; | |
139 public ObjectMapper ObjectMapper; | |
140 public SqlTable SqlTable; | |
141 | |
142 #endregion | |
143 | |
144 #region Init | |
145 | |
146 public TableContext(ExpressionBuilder builder, BuildInfo buildInfo, Type originalType) | |
147 { | |
148 Builder = builder; | |
149 Parent = buildInfo.Parent; | |
150 Expression = buildInfo.Expression; | |
151 SqlQuery = buildInfo.SqlQuery; | |
152 | |
153 OriginalType = originalType; | |
154 ObjectType = GetObjectType(); | |
155 SqlTable = new SqlTable(builder.MappingSchema, ObjectType); | |
156 ObjectMapper = Builder.MappingSchema.GetObjectMapper(ObjectType); | |
157 | |
158 SqlQuery.From.Table(SqlTable); | |
159 | |
160 Init(); | |
161 } | |
162 | |
163 protected TableContext(ExpressionBuilder builder, SqlQuery sqlQuery) | |
164 { | |
165 Builder = builder; | |
166 SqlQuery = sqlQuery; | |
167 } | |
168 | |
169 public TableContext(ExpressionBuilder builder, BuildInfo buildInfo) | |
170 { | |
171 Builder = builder; | |
172 Parent = buildInfo.Parent; | |
173 Expression = buildInfo.Expression; | |
174 SqlQuery = buildInfo.SqlQuery; | |
175 | |
176 var mc = (MethodCallExpression)Expression; | |
177 var attr = builder.GetTableFunctionAttribute(mc.Method); | |
178 | |
179 if (!mc.Method.ReturnType.IsGenericType || mc.Method.ReturnType.GetGenericTypeDefinition() != typeof(Table<>)) | |
180 throw new LinqException("Table function has to return Table<T>."); | |
181 | |
182 OriginalType = mc.Method.ReturnType.GetGenericArguments()[0]; | |
183 ObjectType = GetObjectType(); | |
184 SqlTable = new SqlTable(builder.MappingSchema, ObjectType); | |
185 ObjectMapper = Builder.MappingSchema.GetObjectMapper(ObjectType); | |
186 | |
187 SqlQuery.From.Table(SqlTable); | |
188 | |
189 var args = mc.Arguments.Select(a => builder.ConvertToSql(this, a, false)); | |
190 | |
191 attr.SetTable(SqlTable, mc.Method, mc.Arguments, args); | |
192 | |
193 Init(); | |
194 } | |
195 | |
196 protected Type GetObjectType() | |
197 { | |
198 for (var type = OriginalType.BaseType; type != null && type != typeof(object); type = type.BaseType) | |
199 { | |
200 var extension = TypeExtension.GetTypeExtension(type, Builder.MappingSchema.Extensions); | |
201 var mapping = Builder.MappingSchema.MetadataProvider.GetInheritanceMapping(type, extension); | |
202 | |
203 if (mapping.Length > 0) | |
204 return type; | |
205 } | |
206 | |
207 return OriginalType; | |
208 } | |
209 | |
210 public List<InheritanceMappingAttribute> InheritanceMapping; | |
211 public List<string> InheritanceDiscriminators; | |
212 | |
213 protected void Init() | |
214 { | |
215 Builder.Contexts.Add(this); | |
216 | |
217 InheritanceMapping = ObjectMapper.InheritanceMapping; | |
218 | |
219 if (InheritanceMapping.Count > 0) | |
220 InheritanceDiscriminators = GetInheritanceDiscriminators(Builder, SqlTable, ObjectType, InheritanceMapping); | |
221 | |
222 // Original table is a parent. | |
223 // | |
224 if (ObjectType != OriginalType) | |
225 { | |
226 var predicate = Builder.MakeIsPredicate(this, OriginalType); | |
227 | |
228 if (predicate.GetType() != typeof(SqlQuery.Predicate.Expr)) | |
229 SqlQuery.Where.SearchCondition.Conditions.Add(new SqlQuery.Condition(false, predicate)); | |
230 } | |
231 } | |
232 | |
233 internal static List<string> GetInheritanceDiscriminators( | |
234 ExpressionBuilder builder, | |
235 SqlTable sqlTable, | |
236 Type objectType, | |
237 List<InheritanceMappingAttribute> inheritanceMapping) | |
238 { | |
239 var inheritanceDiscriminators = new List<string>(inheritanceMapping.Count); | |
240 | |
241 foreach (var mapping in inheritanceMapping) | |
242 { | |
243 string discriminator = null; | |
244 | |
245 foreach (MemberMapper mm in builder.MappingSchema.GetObjectMapper(mapping.Type)) | |
246 { | |
247 if (mm.MapMemberInfo.SqlIgnore == false && !sqlTable.Fields.Any(f => f.Value.Name == mm.MemberName)) | |
248 { | |
249 var field = new SqlField(mm.Type, mm.MemberName, mm.Name, mm.MapMemberInfo.Nullable, int.MinValue, null, mm); | |
250 sqlTable.Fields.Add(field); | |
251 } | |
252 | |
253 if (mm.MapMemberInfo.IsInheritanceDiscriminator) | |
254 discriminator = mm.MapMemberInfo.MemberName; | |
255 } | |
256 | |
257 inheritanceDiscriminators.Add(discriminator); | |
258 } | |
259 | |
260 var dname = inheritanceDiscriminators.FirstOrDefault(s => s != null); | |
261 | |
262 if (dname == null) | |
263 throw new LinqException("Inheritance Discriminator is not defined for the '{0}' hierarchy.", objectType); | |
264 | |
265 for (var i = 0; i < inheritanceDiscriminators.Count; i++) | |
266 if (inheritanceDiscriminators[i] == null) | |
267 inheritanceDiscriminators[i] = dname; | |
268 | |
269 return inheritanceDiscriminators; | |
270 } | |
271 | |
272 #endregion | |
273 | |
274 #region BuildQuery | |
275 | |
276 class MappingData | |
277 { | |
278 public MappingSchema MappingSchema; | |
279 public ObjectMapper ObjectMapper; | |
280 public int[] Index; | |
281 public IValueMapper[] ValueMappers; | |
282 } | |
283 | |
284 static object MapDataReaderToObject1(IDataReader dataReader, MappingData data) | |
285 { | |
286 var source = data.MappingSchema.CreateDataReaderMapper(dataReader); | |
287 var destObject = data.ObjectMapper.CreateInstance(); | |
288 | |
289 if (data.ValueMappers == null) | |
290 { | |
291 var mappers = new IValueMapper[data.Index.Length]; | |
292 | |
293 for (var i = 0; i < data.Index.Length; i++) | |
294 { | |
295 var n = data.Index[i]; | |
296 | |
297 if (n < 0) | |
298 continue; | |
299 | |
300 if (!data.ObjectMapper.SupportsTypedValues(i)) | |
301 { | |
302 mappers[i] = data.MappingSchema.DefaultValueMapper; | |
303 continue; | |
304 } | |
305 | |
306 var sourceType = source. GetFieldType(n) ?? typeof(object); | |
307 var destType = data.ObjectMapper.GetFieldType(i) ?? typeof(object); | |
308 | |
309 IValueMapper t; | |
310 | |
311 if (sourceType == destType) | |
312 { | |
313 lock (data.MappingSchema.SameTypeMappers) | |
314 if (!data.MappingSchema.SameTypeMappers.TryGetValue(sourceType, out t)) | |
315 data.MappingSchema.SameTypeMappers.Add(sourceType, t = data.MappingSchema.GetValueMapper(sourceType, destType)); | |
316 } | |
317 else | |
318 { | |
319 var key = new KeyValuePair<Type,Type>(sourceType, destType); | |
320 | |
321 lock (data.MappingSchema.DifferentTypeMappers) | |
322 if (!data.MappingSchema.DifferentTypeMappers.TryGetValue(key, out t)) | |
323 data.MappingSchema.DifferentTypeMappers.Add(key, t = data.MappingSchema.GetValueMapper(sourceType, destType)); | |
324 } | |
325 | |
326 mappers[i] = t; | |
327 } | |
328 | |
329 data.ValueMappers = mappers; | |
330 } | |
331 | |
332 var dest = data.ObjectMapper; | |
333 var idx = data.Index; | |
334 var ms = data.ValueMappers; | |
335 | |
336 for (var i = 0; i < idx.Length; i++) | |
337 { | |
338 var n = idx[i]; | |
339 | |
340 if (n >= 0) | |
341 ms[i].Map(source, dataReader, n, dest, destObject, i); | |
342 } | |
343 | |
344 return destObject; | |
345 } | |
346 | |
347 static object MapDataReaderToObject2(IDataReader dataReader, MappingData data) | |
348 { | |
349 var source = data.MappingSchema.CreateDataReaderMapper(dataReader); | |
350 | |
351 var initContext = new InitContext | |
352 { | |
353 MappingSchema = data.MappingSchema, | |
354 DataSource = source, | |
355 SourceObject = dataReader, | |
356 ObjectMapper = data.ObjectMapper | |
357 }; | |
358 | |
359 var destObject = data.ObjectMapper.CreateInstance(initContext); | |
360 | |
361 if (initContext.StopMapping) | |
362 return destObject; | |
363 | |
364 var smDest = destObject as ISupportMapping; | |
365 | |
366 if (smDest != null) | |
367 { | |
368 smDest.BeginMapping(initContext); | |
369 | |
370 if (initContext.StopMapping) | |
371 return destObject; | |
372 } | |
373 | |
374 if (data.ValueMappers == null) | |
375 { | |
376 var mappers = new IValueMapper[data.Index.Length]; | |
377 | |
378 for (var i = 0; i < data.Index.Length; i++) | |
379 { | |
380 var n = data.Index[i]; | |
381 | |
382 if (n < 0) | |
383 continue; | |
384 | |
385 if (!data.ObjectMapper.SupportsTypedValues(i)) | |
386 { | |
387 mappers[i] = data.MappingSchema.DefaultValueMapper; | |
388 continue; | |
389 } | |
390 | |
391 var sourceType = source. GetFieldType(n) ?? typeof(object); | |
392 var destType = data.ObjectMapper.GetFieldType(i) ?? typeof(object); | |
393 | |
394 IValueMapper t; | |
395 | |
396 if (sourceType == destType) | |
397 { | |
398 lock (data.MappingSchema.SameTypeMappers) | |
399 if (!data.MappingSchema.SameTypeMappers.TryGetValue(sourceType, out t)) | |
400 data.MappingSchema.SameTypeMappers.Add(sourceType, t = data.MappingSchema.GetValueMapper(sourceType, destType)); | |
401 } | |
402 else | |
403 { | |
404 var key = new KeyValuePair<Type,Type>(sourceType, destType); | |
405 | |
406 lock (data.MappingSchema.DifferentTypeMappers) | |
407 if (!data.MappingSchema.DifferentTypeMappers.TryGetValue(key, out t)) | |
408 data.MappingSchema.DifferentTypeMappers.Add(key, t = data.MappingSchema.GetValueMapper(sourceType, destType)); | |
409 } | |
410 | |
411 mappers[i] = t; | |
412 } | |
413 | |
414 data.ValueMappers = mappers; | |
415 } | |
416 | |
417 var dest = data.ObjectMapper; | |
418 var idx = data.Index; | |
419 var ms = data.ValueMappers; | |
420 | |
421 for (var i = 0; i < idx.Length; i++) | |
422 { | |
423 var n = idx[i]; | |
424 | |
425 if (n >= 0) | |
426 ms[i].Map(source, dataReader, n, dest, destObject, i); | |
427 } | |
428 | |
429 if (smDest != null) | |
430 smDest.EndMapping(initContext); | |
431 | |
432 return destObject; | |
433 } | |
434 | |
435 static object DefaultInheritanceMappingException(object value, Type type) | |
436 { | |
437 throw new LinqException("Inheritance mapping is not defined for discriminator value '{0}' in the '{1}' hierarchy.", value, type); | |
438 } | |
439 | |
440 static readonly MethodInfo _mapperMethod1 = ReflectionHelper.Expressor<object>.MethodExpressor(_ => MapDataReaderToObject1(null, null)); | |
441 static readonly MethodInfo _mapperMethod2 = ReflectionHelper.Expressor<object>.MethodExpressor(_ => MapDataReaderToObject2(null, null)); | |
442 | |
443 #if FW4 || SILVERLIGHT | |
444 ParameterExpression _variable; | |
445 static int _varIndex; | |
446 #endif | |
447 | |
448 Expression BuildTableExpression(bool buildBlock, Type objectType, int[] index) | |
449 { | |
450 #if FW4 || SILVERLIGHT | |
451 if (buildBlock && _variable != null) | |
452 return _variable; | |
453 #endif | |
454 | |
455 var data = new MappingData | |
456 { | |
457 MappingSchema = Builder.MappingSchema, | |
458 ObjectMapper = Builder.MappingSchema.GetObjectMapper(objectType), | |
459 Index = index | |
460 }; | |
461 | |
462 Expression expr; | |
463 | |
464 if (Builder.DataContextInfo.DataContext == null || | |
465 TypeHelper.IsSameOrParent(typeof(ISupportMapping), objectType) || | |
466 TypeHelper.GetFirstAttribute(objectType, typeof(ObjectFactoryAttribute)) != null) | |
467 { | |
468 expr = Expression.Convert( | |
469 Expression.Call(null, _mapperMethod2, | |
470 ExpressionBuilder.DataReaderParam, | |
471 Expression.Constant(data)), | |
472 objectType); | |
473 } | |
474 else | |
475 { | |
476 expr = Expression.Convert( | |
477 Expression.Call(null, _mapperMethod1, | |
478 ExpressionBuilder.DataReaderParam, | |
479 Expression.Constant(data)), | |
480 objectType); | |
481 } | |
482 | |
483 expr = ProcessExpression(expr); | |
484 | |
485 #if FW4 || SILVERLIGHT | |
486 | |
487 if (!buildBlock) | |
488 return expr; | |
489 | |
490 Builder.BlockVariables. Add(_variable = Expression.Variable(expr.Type, expr.Type.Name + _varIndex++)); | |
491 Builder.BlockExpressions.Add(Expression.Assign(_variable, expr)); | |
492 | |
493 return _variable; | |
494 | |
495 #else | |
496 return expr; | |
497 #endif | |
498 } | |
499 | |
500 protected virtual Expression ProcessExpression(Expression expression) | |
501 { | |
502 return expression; | |
503 } | |
504 | |
505 int[] BuildIndex(int[] index, Type objectType) | |
506 { | |
507 var names = new Dictionary<string,int>(); | |
508 var n = 0; | |
509 | |
510 foreach (MemberMapper mm in Builder.MappingSchema.GetObjectMapper(objectType)) | |
511 if (mm.MapMemberInfo.SqlIgnore == false) | |
512 names.Add(mm.MemberName, n++); | |
513 | |
514 var q = | |
515 from r in SqlTable.Fields.Values.Select((f,i) => new { f, i }) | |
516 where names.ContainsKey(r.f.Name) | |
517 orderby names[r.f.Name] | |
518 select index[r.i]; | |
519 | |
520 return q.ToArray(); | |
521 } | |
522 | |
523 Expression BuildQuery(Type tableType) | |
524 { | |
525 SqlInfo[] info; | |
526 | |
527 if (ObjectType == tableType) | |
528 { | |
529 info = ConvertToIndex(null, 0, ConvertFlags.All); | |
530 } | |
531 else | |
532 { | |
533 info = ConvertToSql(null, 0, ConvertFlags.All); | |
534 | |
535 var table = new SqlTable(Builder.MappingSchema, tableType); | |
536 var q = | |
537 from fld1 in table.Fields.Values.Select((f,i) => new { f, i }) | |
538 join fld2 in info on fld1.f.Name equals ((SqlField)fld2.Sql).Name | |
539 orderby fld1.i | |
540 select GetIndex(fld2); | |
541 | |
542 info = q.ToArray(); | |
543 } | |
544 | |
545 var index = info.Select(idx => ConvertToParentIndex(idx.Index, null)).ToArray(); | |
546 | |
547 if (ObjectType != tableType || InheritanceMapping.Count == 0) | |
548 return BuildTableExpression(!Builder.IsBlockDisable, tableType, index); | |
549 | |
550 Expression expr; | |
551 | |
552 var defaultMapping = InheritanceMapping.SingleOrDefault(m => m.IsDefault); | |
553 | |
554 if (defaultMapping != null) | |
555 { | |
556 expr = Expression.Convert( | |
557 BuildTableExpression(false, defaultMapping.Type, BuildIndex(index, defaultMapping.Type)), | |
558 ObjectType); | |
559 } | |
560 else | |
561 { | |
562 var exceptionMethod = ReflectionHelper.Expressor<object>.MethodExpressor(_ => DefaultInheritanceMappingException(null, null)); | |
563 var dindex = | |
564 ( | |
565 from f in SqlTable.Fields.Values | |
566 where f.Name == InheritanceDiscriminators[0] | |
567 select ConvertToParentIndex(_indexes[f].Index, null) | |
568 ).First(); | |
569 | |
570 expr = Expression.Convert( | |
571 Expression.Call(null, exceptionMethod, | |
572 Expression.Call( | |
573 ExpressionBuilder.DataReaderParam, | |
574 ReflectionHelper.DataReader.GetValue, | |
575 Expression.Constant(dindex)), | |
576 Expression.Constant(ObjectType)), | |
577 ObjectType); | |
578 } | |
579 | |
580 foreach (var mapping in InheritanceMapping.Select((m,i) => new { m, i }).Where(m => m.m != defaultMapping)) | |
581 { | |
582 var dindex = | |
583 ( | |
584 from f in SqlTable.Fields.Values | |
585 where f.Name == InheritanceDiscriminators[mapping.i] | |
586 select ConvertToParentIndex(_indexes[f].Index, null) | |
587 ).First(); | |
588 | |
589 Expression testExpr; | |
590 | |
591 if (mapping.m.Code == null) | |
592 { | |
593 testExpr = Expression.Call( | |
594 ExpressionBuilder.DataReaderParam, | |
595 ReflectionHelper.DataReader.IsDBNull, | |
596 Expression.Constant(dindex)); | |
597 } | |
598 else | |
599 { | |
600 var codeType = mapping.m.Code.GetType(); | |
601 | |
602 testExpr = Expression.Equal( | |
603 Expression.Constant(mapping.m.Code), | |
604 Builder.BuildSql(codeType, dindex)); | |
605 } | |
606 | |
607 expr = Expression.Condition( | |
608 testExpr, | |
609 Expression.Convert(BuildTableExpression(false, mapping.m.Type, BuildIndex(index, mapping.m.Type)), ObjectType), | |
610 expr); | |
611 } | |
612 | |
613 return expr; | |
614 } | |
615 | |
616 public void BuildQuery<T>(Query<T> query, ParameterExpression queryParameter) | |
617 { | |
618 var expr = BuildQuery(typeof(T)); | |
619 var mapper = Builder.BuildMapper<T>(expr); | |
620 | |
621 query.SetQuery(mapper.Compile()); | |
622 } | |
623 | |
624 #endregion | |
625 | |
626 #region BuildExpression | |
627 | |
628 public Expression BuildExpression(Expression expression, int level) | |
629 { | |
630 // Build table. | |
631 // | |
632 var table = FindTable(expression, level, false); | |
633 | |
634 if (table == null) | |
635 { | |
636 if (expression is MemberExpression) | |
637 { | |
638 var memberExpression = (MemberExpression)expression; | |
639 | |
640 if (ObjectMapper != null && | |
641 ObjectMapper.TypeAccessor.OriginalType == memberExpression.Member.DeclaringType) | |
642 { | |
643 throw new LinqException("Member '{0}.{1}' is not a table column.", | |
644 memberExpression.Member.Name, memberExpression.Member.Name); | |
645 } | |
646 } | |
647 | |
648 throw new InvalidOperationException(); | |
649 } | |
650 | |
651 if (table.Field == null) | |
652 return table.Table.BuildQuery(table.Table.OriginalType); | |
653 | |
654 // Build field. | |
655 // | |
656 var info = ConvertToIndex(expression, level, ConvertFlags.Field).Single(); | |
657 var idx = ConvertToParentIndex(info.Index, null); | |
658 | |
659 if (expression is MemberExpression) | |
660 { | |
661 var me = (MemberExpression)expression; | |
662 var memberAccessor = TypeAccessor.GetAccessor(me.Member.DeclaringType)[me.Member.Name]; | |
663 return Builder.BuildSql(memberAccessor, idx); | |
664 } | |
665 else | |
666 { | |
667 return Builder.BuildSql(expression.Type, idx); | |
668 } | |
669 } | |
670 | |
671 #endregion | |
672 | |
673 #region ConvertToSql | |
674 | |
675 public SqlInfo[] ConvertToSql(Expression expression, int level, ConvertFlags flags) | |
676 { | |
677 switch (flags) | |
678 { | |
679 case ConvertFlags.All : | |
680 { | |
681 var table = FindTable(expression, level, false); | |
682 | |
683 if (table.Field == null) | |
684 return table.Table.SqlTable.Fields.Values | |
685 .Select(_ => new SqlInfo(_.MemberMapper.MemberAccessor.MemberInfo) { Sql = _ }) | |
686 .ToArray(); | |
687 | |
688 break; | |
689 } | |
690 | |
691 case ConvertFlags.Key : | |
692 { | |
693 var table = FindTable(expression, level, false); | |
694 | |
695 if (table.Field == null) | |
696 { | |
697 var q = | |
698 from f in table.Table.SqlTable.Fields.Values | |
699 where f.IsPrimaryKey | |
700 orderby f.PrimaryKeyOrder | |
701 select new SqlInfo(f.MemberMapper.MemberAccessor.MemberInfo) { Sql = f }; | |
702 | |
703 var key = q.ToArray(); | |
704 | |
705 return key.Length != 0 ? key : ConvertToSql(expression, level, ConvertFlags.All); | |
706 } | |
707 | |
708 break; | |
709 } | |
710 | |
711 case ConvertFlags.Field : | |
712 { | |
713 var table = FindTable(expression, level, true); | |
714 | |
715 if (table.Field != null) | |
716 return new[] | |
717 { | |
718 new SqlInfo(table.Field.MemberMapper.MemberAccessor.MemberInfo) { Sql = table.Field } | |
719 }; | |
720 | |
721 break; | |
722 } | |
723 } | |
724 | |
725 throw new InvalidOperationException(); | |
726 } | |
727 | |
728 #endregion | |
729 | |
730 #region ConvertToIndex | |
731 | |
732 readonly Dictionary<ISqlExpression,SqlInfo> _indexes = new Dictionary<ISqlExpression,SqlInfo>(); | |
733 | |
734 protected SqlInfo GetIndex(SqlInfo expr) | |
735 { | |
736 SqlInfo n; | |
737 | |
738 if (_indexes.TryGetValue(expr.Sql, out n)) | |
739 return n; | |
740 | |
741 if (expr.Sql is SqlField) | |
742 { | |
743 var field = (SqlField)expr.Sql; | |
744 expr.Index = SqlQuery.Select.Add(field, field.Alias); | |
745 } | |
746 else | |
747 { | |
748 expr.Index = SqlQuery.Select.Add(expr.Sql); | |
749 } | |
750 | |
751 expr.Query = SqlQuery; | |
752 | |
753 _indexes.Add(expr.Sql, expr); | |
754 | |
755 return expr; | |
756 } | |
757 | |
758 public SqlInfo[] ConvertToIndex(Expression expression, int level, ConvertFlags flags) | |
759 { | |
760 switch (flags) | |
761 { | |
762 case ConvertFlags.Field : | |
763 case ConvertFlags.Key : | |
764 case ConvertFlags.All : | |
765 | |
766 var info = ConvertToSql(expression, level, flags); | |
767 | |
768 for (var i = 0; i < info.Length; i++) | |
769 info[i] = GetIndex(info[i]); | |
770 | |
771 return info; | |
772 } | |
773 | |
774 throw new InvalidOperationException(); | |
775 } | |
776 | |
777 #endregion | |
778 | |
779 #region IsExpression | |
780 | |
781 public IsExpressionResult IsExpression(Expression expression, int level, RequestFor requestFor) | |
782 { | |
783 switch (requestFor) | |
784 { | |
785 case RequestFor.Field : | |
786 { | |
787 var table = FindTable(expression, level, false); | |
788 return new IsExpressionResult(table != null && table.Field != null); | |
789 } | |
790 | |
791 case RequestFor.Table : | |
792 case RequestFor.Object : | |
793 { | |
794 var table = FindTable(expression, level, false); | |
795 var isTable = | |
796 table != null && | |
797 table.Field == null && | |
798 (expression == null || expression.GetLevelExpression(table.Level) == expression); | |
799 | |
800 return new IsExpressionResult(isTable, isTable ? table.Table : null); | |
801 } | |
802 | |
803 case RequestFor.Expression : | |
804 { | |
805 if (expression == null) | |
806 return IsExpressionResult.False; | |
807 | |
808 var levelExpression = expression.GetLevelExpression(level); | |
809 | |
810 switch (levelExpression.NodeType) | |
811 { | |
812 case ExpressionType.MemberAccess : | |
813 case ExpressionType.Parameter : | |
814 case ExpressionType.Call : | |
815 | |
816 var table = FindTable(expression, level, false); | |
817 return new IsExpressionResult(table == null); | |
818 } | |
819 | |
820 return IsExpressionResult.True; | |
821 } | |
822 | |
823 case RequestFor.Association : | |
824 { | |
825 if (ObjectMapper.Associations.Count > 0) | |
826 { | |
827 var table = FindTable(expression, level, false); | |
828 var isat = | |
829 table != null && | |
830 table.Table is AssociatedTableContext && | |
831 table.Field == null && | |
832 (expression == null || expression.GetLevelExpression(table.Level) == expression); | |
833 | |
834 return new IsExpressionResult(isat, isat ? table.Table : null); | |
835 } | |
836 | |
837 return IsExpressionResult.False; | |
838 } | |
839 } | |
840 | |
841 return IsExpressionResult.False; | |
842 } | |
843 | |
844 #endregion | |
845 | |
846 #region GetContext | |
847 | |
848 interface IAssociationHelper | |
849 { | |
850 Expression GetExpression(Expression parent, AssociatedTableContext association); | |
851 } | |
852 | |
853 class AssociationHelper<T> : IAssociationHelper | |
854 where T : class | |
855 { | |
856 public Expression GetExpression(Expression parent, AssociatedTableContext association) | |
857 { | |
858 Expression expr = null; | |
859 var param = Expression.Parameter(typeof(T), "c"); | |
860 | |
861 foreach (var cond in (association).ParentAssociationJoin.Condition.Conditions) | |
862 { | |
863 var p = (SqlQuery.Predicate.ExprExpr)cond.Predicate; | |
864 var e1 = Expression.MakeMemberAccess(parent, ((SqlField)p.Expr1).MemberMapper.MemberAccessor.MemberInfo); | |
865 var e2 = Expression.MakeMemberAccess(param, ((SqlField)p.Expr2).MemberMapper.MemberAccessor.MemberInfo) as Expression; | |
866 | |
867 while (e1.Type != e2.Type) | |
868 { | |
869 if (TypeHelper.IsNullableType(e1.Type)) | |
870 { | |
871 e1 = Expression.PropertyOrField(e1, "Value"); | |
872 continue; | |
873 } | |
874 | |
875 if (TypeHelper.IsNullableType(e2.Type)) | |
876 { | |
877 e2 = Expression.PropertyOrField(e2, "Value"); | |
878 continue; | |
879 } | |
880 | |
881 e2 = Expression.Convert(e2, e1.Type); | |
882 } | |
883 | |
884 var ex = Expression.Equal(e1, e2); | |
885 | |
886 expr = expr == null ? ex : Expression.AndAlso(expr, ex); | |
887 } | |
888 | |
889 var predicate = Expression.Lambda<Func<T,bool>>(expr, param); | |
890 | |
891 return association.Builder.DataContextInfo.DataContext.GetTable<T>().Where(predicate).Expression; | |
892 } | |
893 } | |
894 | |
895 public IBuildContext GetContext(Expression expression, int level, BuildInfo buildInfo) | |
896 { | |
897 if (expression == null) | |
898 { | |
899 if (buildInfo != null && buildInfo.IsSubQuery) | |
900 { | |
901 var table = new TableContext( | |
902 Builder, | |
903 new BuildInfo(Parent is SelectManyBuilder.SelectManyContext ? this : Parent, Expression, buildInfo.SqlQuery), | |
904 SqlTable.ObjectType); | |
905 | |
906 return table; | |
907 } | |
908 | |
909 return this; | |
910 } | |
911 | |
912 if (ObjectMapper.Associations.Count > 0) | |
913 { | |
914 var levelExpression = expression.GetLevelExpression(level); | |
915 | |
916 if (buildInfo != null && buildInfo.IsSubQuery) | |
917 { | |
918 if (levelExpression == expression && expression.NodeType == ExpressionType.MemberAccess) | |
919 { | |
920 var tableLevel = GetAssociation(expression, level); | |
921 var association = (AssociatedTableContext)tableLevel.Table; | |
922 | |
923 if (association.IsList) | |
924 { | |
925 var ma = (MemberExpression)buildInfo.Expression; | |
926 var atype = typeof(AssociationHelper<>).MakeGenericType(association.ObjectType); | |
927 var helper = (IAssociationHelper)Activator.CreateInstance(atype); | |
928 var expr = helper.GetExpression(ma.Expression, association); | |
929 | |
930 buildInfo.IsAssociationBuilt = true; | |
931 | |
932 if (tableLevel.IsNew || buildInfo.CopyTable) | |
933 association.ParentAssociationJoin.IsWeak = true; | |
934 | |
935 return Builder.BuildSequence(new BuildInfo(buildInfo, expr)); | |
936 } | |
937 } | |
938 else | |
939 { | |
940 var association = GetAssociation(levelExpression, level); | |
941 var paj = ((AssociatedTableContext)association.Table).ParentAssociationJoin; | |
942 | |
943 paj.IsWeak = paj.IsWeak && buildInfo.CopyTable; | |
944 | |
945 return association.Table.GetContext(expression, level + 1, buildInfo); | |
946 } | |
947 } | |
948 } | |
949 | |
950 throw new InvalidOperationException(); | |
951 } | |
952 | |
953 #endregion | |
954 | |
955 #region ConvertToParentIndex | |
956 | |
957 public int ConvertToParentIndex(int index, IBuildContext context) | |
958 { | |
959 return Parent == null ? index : Parent.ConvertToParentIndex(index, this); | |
960 } | |
961 | |
962 #endregion | |
963 | |
964 #region SetAlias | |
965 | |
966 public void SetAlias(string alias) | |
967 { | |
968 if (alias.Contains('<')) | |
969 return; | |
970 | |
971 if (SqlTable.Alias == null) | |
972 SqlTable.Alias = alias; | |
973 } | |
974 | |
975 #endregion | |
976 | |
977 #region GetSubQuery | |
978 | |
979 public ISqlExpression GetSubQuery(IBuildContext context) | |
980 { | |
981 return null; | |
982 } | |
983 | |
984 #endregion | |
985 | |
986 #region Helpers | |
987 | |
988 SqlField GetField(Expression expression, int level, bool throwException) | |
989 { | |
990 if (expression.NodeType == ExpressionType.MemberAccess) | |
991 { | |
992 var memberExpression = (MemberExpression)expression; | |
993 var levelExpression = expression.GetLevelExpression(level); | |
994 | |
995 if (levelExpression.NodeType == ExpressionType.MemberAccess) | |
996 { | |
997 if (levelExpression != expression) | |
998 { | |
999 var levelMember = (MemberExpression)levelExpression; | |
1000 | |
1001 if (TypeHelper.IsNullableValueMember(memberExpression.Member) && memberExpression.Expression == levelExpression) | |
1002 memberExpression = levelMember; | |
1003 else | |
1004 { | |
1005 var sameType = | |
1006 levelMember.Member.ReflectedType == SqlTable.ObjectType || | |
1007 levelMember.Member.DeclaringType == SqlTable.ObjectType; | |
1008 | |
1009 if (!sameType) | |
1010 { | |
1011 var mi = SqlTable.ObjectType.GetMember(levelMember.Member.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); | |
1012 sameType = mi.Any(_ => _.DeclaringType == levelMember.Member.DeclaringType); | |
1013 } | |
1014 | |
1015 if (sameType || InheritanceMapping.Count > 0) | |
1016 { | |
1017 foreach (var field in SqlTable.Fields.Values) | |
1018 { | |
1019 if (field.MemberMapper is MemberMapper.ComplexMapper) | |
1020 { | |
1021 var name = levelMember.Member.Name; | |
1022 | |
1023 for (var ex = (MemberExpression)expression; ex != levelMember; ex = (MemberExpression)ex.Expression) | |
1024 name += "." + ex.Member.Name; | |
1025 | |
1026 if (field.MemberMapper.MemberName == name) | |
1027 return field; | |
1028 } | |
1029 } | |
1030 } | |
1031 } | |
1032 } | |
1033 | |
1034 if (levelExpression == memberExpression) | |
1035 { | |
1036 foreach (var field in SqlTable.Fields.Values) | |
1037 { | |
1038 if (TypeHelper.Equals(field.MemberMapper.MapMemberInfo.MemberAccessor.MemberInfo, memberExpression.Member, SqlTable.ObjectType)) | |
1039 { | |
1040 if (field.MemberMapper is MemberMapper.ComplexMapper && | |
1041 field.MemberMapper.MemberName.IndexOf('.') > 0) | |
1042 { | |
1043 var name = memberExpression.Member.Name; | |
1044 var me = memberExpression; | |
1045 | |
1046 if (!(me.Expression is MemberExpression)) | |
1047 return null; | |
1048 | |
1049 while (me.Expression is MemberExpression) | |
1050 { | |
1051 me = (MemberExpression)me.Expression; | |
1052 name = me.Member.Name + '.' + name; | |
1053 } | |
1054 | |
1055 return SqlTable.Fields.Values.FirstOrDefault(f => f.MemberMapper.MemberName == name); | |
1056 } | |
1057 | |
1058 return field; | |
1059 } | |
1060 | |
1061 if (InheritanceMapping.Count > 0 && field.Name == memberExpression.Member.Name) | |
1062 foreach (var mapping in InheritanceMapping) | |
1063 foreach (MemberMapper mm in Builder.MappingSchema.GetObjectMapper(mapping.Type)) | |
1064 if (TypeHelper.Equals(mm.MapMemberInfo.MemberAccessor.MemberInfo, memberExpression.Member)) | |
1065 return field; | |
1066 } | |
1067 | |
1068 if (throwException && | |
1069 ObjectMapper != null && | |
1070 ObjectMapper.TypeAccessor.OriginalType == memberExpression.Member.DeclaringType) | |
1071 { | |
1072 throw new LinqException("Member '{0}.{1}' is not a table column.", | |
1073 memberExpression.Member.DeclaringType.Name, memberExpression.Member.Name); | |
1074 } | |
1075 } | |
1076 } | |
1077 } | |
1078 | |
1079 return null; | |
1080 } | |
1081 | |
1082 [JetBrains.Annotations.NotNull] | |
1083 readonly Dictionary<MemberInfo,AssociatedTableContext> _associations = | |
1084 new Dictionary<MemberInfo,AssociatedTableContext>(new MemberInfoComparer()); | |
1085 | |
1086 class TableLevel | |
1087 { | |
1088 public TableContext Table; | |
1089 public SqlField Field; | |
1090 public int Level; | |
1091 public bool IsNew; | |
1092 } | |
1093 | |
1094 TableLevel FindTable(Expression expression, int level, bool throwException) | |
1095 { | |
1096 if (expression == null) | |
1097 return new TableLevel { Table = this }; | |
1098 | |
1099 var levelExpression = expression.GetLevelExpression(level); | |
1100 | |
1101 switch (levelExpression.NodeType) | |
1102 { | |
1103 case ExpressionType.MemberAccess : | |
1104 case ExpressionType.Parameter : | |
1105 { | |
1106 var field = GetField(expression, level, throwException); | |
1107 | |
1108 if (field != null || (level == 0 && levelExpression == expression)) | |
1109 return new TableLevel { Table = this, Field = field, Level = level }; | |
1110 | |
1111 return GetAssociation(expression, level); | |
1112 } | |
1113 } | |
1114 | |
1115 return null; | |
1116 } | |
1117 | |
1118 TableLevel GetAssociation(Expression expression, int level) | |
1119 { | |
1120 var objectMapper = ObjectMapper; | |
1121 var levelExpression = expression.GetLevelExpression(level); | |
1122 var inheritance = | |
1123 ( | |
1124 from m in InheritanceMapping | |
1125 let om = Builder.MappingSchema.GetObjectMapper(m.Type) | |
1126 where om.Associations.Count > 0 | |
1127 select om | |
1128 ).ToList(); | |
1129 | |
1130 if (objectMapper.Associations.Count > 0 || inheritance.Count > 0) | |
1131 { | |
1132 if (levelExpression.NodeType == ExpressionType.MemberAccess) | |
1133 { | |
1134 var memberExpression = (MemberExpression)levelExpression; | |
1135 var isNew = false; | |
1136 | |
1137 AssociatedTableContext tableAssociation; | |
1138 | |
1139 if (!_associations.TryGetValue(memberExpression.Member, out tableAssociation)) | |
1140 { | |
1141 var q = | |
1142 from a in objectMapper.Associations.Concat(inheritance.SelectMany(om => om.Associations)) | |
1143 where TypeHelper.Equals(a.MemberAccessor.MemberInfo, memberExpression.Member) | |
1144 select new AssociatedTableContext(Builder, this, a) { Parent = Parent }; | |
1145 | |
1146 tableAssociation = q.FirstOrDefault(); | |
1147 | |
1148 isNew = true; | |
1149 | |
1150 _associations.Add(memberExpression.Member, tableAssociation); | |
1151 } | |
1152 | |
1153 if (tableAssociation != null) | |
1154 { | |
1155 if (levelExpression == expression) | |
1156 return new TableLevel { Table = tableAssociation, Level = level, IsNew = isNew }; | |
1157 | |
1158 var al = tableAssociation.GetAssociation(expression, level + 1); | |
1159 | |
1160 if (al != null) | |
1161 return al; | |
1162 | |
1163 var field = tableAssociation.GetField(expression, level + 1, false); | |
1164 | |
1165 return new TableLevel { Table = tableAssociation, Field = field, Level = field == null ? level : level + 1, IsNew = isNew }; | |
1166 } | |
1167 } | |
1168 } | |
1169 | |
1170 return null; | |
1171 } | |
1172 | |
1173 #endregion | |
1174 } | |
1175 | |
1176 #endregion | |
1177 | |
1178 #region AssociatedTableContext | |
1179 | |
1180 public class AssociatedTableContext : TableContext | |
1181 { | |
1182 public readonly TableContext ParentAssociation; | |
1183 public readonly SqlQuery.JoinedTable ParentAssociationJoin; | |
1184 public readonly Association Association; | |
1185 public readonly bool IsList; | |
1186 | |
1187 public override IBuildContext Parent | |
1188 { | |
1189 get { return ParentAssociation.Parent; } | |
1190 set { } | |
1191 } | |
1192 | |
1193 public AssociatedTableContext(ExpressionBuilder builder, TableContext parent, Association association) | |
1194 : base(builder, parent.SqlQuery) | |
1195 { | |
1196 var type = TypeHelper.GetMemberType(association.MemberAccessor.MemberInfo); | |
1197 var left = association.CanBeNull; | |
1198 | |
1199 if (TypeHelper.IsSameOrParent(typeof(IEnumerable), type)) | |
1200 { | |
1201 var etypes = TypeHelper.GetGenericArguments(type, typeof(IEnumerable)); | |
1202 type = etypes != null && etypes.Length > 0 ? etypes[0] : TypeHelper.GetListItemType(type); | |
1203 IsList = true; | |
1204 } | |
1205 | |
1206 OriginalType = type; | |
1207 ObjectType = GetObjectType(); | |
1208 ObjectMapper = Builder.MappingSchema.GetObjectMapper(ObjectType); | |
1209 SqlTable = new SqlTable(builder.MappingSchema, ObjectType); | |
1210 | |
1211 var psrc = parent.SqlQuery.From[parent.SqlTable]; | |
1212 var join = left ? SqlTable.WeakLeftJoin() : IsList ? SqlTable.InnerJoin() : SqlTable.WeakInnerJoin(); | |
1213 | |
1214 Association = association; | |
1215 ParentAssociation = parent; | |
1216 ParentAssociationJoin = join.JoinedTable; | |
1217 | |
1218 psrc.Joins.Add(join.JoinedTable); | |
1219 | |
1220 for (var i = 0; i < association.ThisKey.Length; i++) | |
1221 { | |
1222 SqlField field1; | |
1223 SqlField field2; | |
1224 | |
1225 if (!parent.SqlTable.Fields.TryGetValue(association.ThisKey[i], out field1)) | |
1226 throw new LinqException("Association key '{0}' not found for type '{1}.", association.ThisKey[i], parent.ObjectType); | |
1227 | |
1228 if (!SqlTable.Fields.TryGetValue(association.OtherKey[i], out field2)) | |
1229 throw new LinqException("Association key '{0}' not found for type '{1}.", association.OtherKey[i], ObjectType); | |
1230 | |
1231 join.Field(field1).Equal.Field(field2); | |
1232 } | |
1233 | |
1234 Init(); | |
1235 } | |
1236 | |
1237 protected override Expression ProcessExpression(Expression expression) | |
1238 { | |
1239 var isLeft = false; | |
1240 | |
1241 for ( | |
1242 var association = this; | |
1243 isLeft == false && association != null; | |
1244 association = association.ParentAssociation as AssociatedTableContext) | |
1245 { | |
1246 isLeft = | |
1247 association.ParentAssociationJoin.JoinType == SqlQuery.JoinType.Left || | |
1248 association.ParentAssociationJoin.JoinType == SqlQuery.JoinType.OuterApply; | |
1249 } | |
1250 | |
1251 if (isLeft) | |
1252 { | |
1253 Expression cond = null; | |
1254 | |
1255 var keys = ConvertToIndex(null, 0, ConvertFlags.Key); | |
1256 | |
1257 foreach (var key in keys) | |
1258 { | |
1259 var index2 = ConvertToParentIndex(key.Index, null); | |
1260 | |
1261 Expression e = Expression.Call( | |
1262 ExpressionBuilder.DataReaderParam, | |
1263 ReflectionHelper.DataReader.IsDBNull, | |
1264 Expression.Constant(index2)); | |
1265 | |
1266 cond = cond == null ? e : Expression.AndAlso(cond, e); | |
1267 } | |
1268 | |
1269 expression = Expression.Condition(cond, Expression.Constant(null, expression.Type), expression); | |
1270 } | |
1271 | |
1272 return expression; | |
1273 } | |
1274 } | |
1275 | |
1276 #endregion | |
1277 } | |
1278 } |