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 }