Mercurial > pub > bltoolkit
annotate Source/Data/Linq/Builder/TableBuilder.cs @ 3:1ef98bd70424
!bug 100 +3h
Исправление проблемы BLToolkit + mono 3.4
author | cin |
---|---|
date | Fri, 22 Aug 2014 17:34:46 +0400 |
parents | 8f65451dc28f |
children |
rev | line source |
---|---|
0 | 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, | |
1
8f65451dc28f
Исправлена проблема с фабрикой и выборкой нескольких объектов в linq выражении
cin
parents:
0
diff
changeset
|
356 ObjectMapper = data.ObjectMapper, |
8f65451dc28f
Исправлена проблема с фабрикой и выборкой нескольких объектов в linq выражении
cin
parents:
0
diff
changeset
|
357 MappingIndex = data.Index |
0 | 358 }; |
359 | |
360 var destObject = data.ObjectMapper.CreateInstance(initContext); | |
361 | |
362 if (initContext.StopMapping) | |
363 return destObject; | |
364 | |
365 var smDest = destObject as ISupportMapping; | |
366 | |
367 if (smDest != null) | |
368 { | |
369 smDest.BeginMapping(initContext); | |
370 | |
371 if (initContext.StopMapping) | |
372 return destObject; | |
373 } | |
374 | |
375 if (data.ValueMappers == null) | |
376 { | |
377 var mappers = new IValueMapper[data.Index.Length]; | |
378 | |
379 for (var i = 0; i < data.Index.Length; i++) | |
380 { | |
381 var n = data.Index[i]; | |
382 | |
383 if (n < 0) | |
384 continue; | |
385 | |
386 if (!data.ObjectMapper.SupportsTypedValues(i)) | |
387 { | |
388 mappers[i] = data.MappingSchema.DefaultValueMapper; | |
389 continue; | |
390 } | |
391 | |
392 var sourceType = source. GetFieldType(n) ?? typeof(object); | |
393 var destType = data.ObjectMapper.GetFieldType(i) ?? typeof(object); | |
394 | |
395 IValueMapper t; | |
396 | |
397 if (sourceType == destType) | |
398 { | |
399 lock (data.MappingSchema.SameTypeMappers) | |
400 if (!data.MappingSchema.SameTypeMappers.TryGetValue(sourceType, out t)) | |
401 data.MappingSchema.SameTypeMappers.Add(sourceType, t = data.MappingSchema.GetValueMapper(sourceType, destType)); | |
402 } | |
403 else | |
404 { | |
405 var key = new KeyValuePair<Type,Type>(sourceType, destType); | |
406 | |
407 lock (data.MappingSchema.DifferentTypeMappers) | |
408 if (!data.MappingSchema.DifferentTypeMappers.TryGetValue(key, out t)) | |
409 data.MappingSchema.DifferentTypeMappers.Add(key, t = data.MappingSchema.GetValueMapper(sourceType, destType)); | |
410 } | |
411 | |
412 mappers[i] = t; | |
413 } | |
414 | |
415 data.ValueMappers = mappers; | |
416 } | |
417 | |
418 var dest = data.ObjectMapper; | |
419 var idx = data.Index; | |
420 var ms = data.ValueMappers; | |
421 | |
422 for (var i = 0; i < idx.Length; i++) | |
423 { | |
424 var n = idx[i]; | |
425 | |
426 if (n >= 0) | |
427 ms[i].Map(source, dataReader, n, dest, destObject, i); | |
428 } | |
429 | |
430 if (smDest != null) | |
431 smDest.EndMapping(initContext); | |
432 | |
433 return destObject; | |
434 } | |
435 | |
436 static object DefaultInheritanceMappingException(object value, Type type) | |
437 { | |
438 throw new LinqException("Inheritance mapping is not defined for discriminator value '{0}' in the '{1}' hierarchy.", value, type); | |
439 } | |
440 | |
441 static readonly MethodInfo _mapperMethod1 = ReflectionHelper.Expressor<object>.MethodExpressor(_ => MapDataReaderToObject1(null, null)); | |
442 static readonly MethodInfo _mapperMethod2 = ReflectionHelper.Expressor<object>.MethodExpressor(_ => MapDataReaderToObject2(null, null)); | |
443 | |
444 #if FW4 || SILVERLIGHT | |
445 ParameterExpression _variable; | |
446 static int _varIndex; | |
447 #endif | |
448 | |
449 Expression BuildTableExpression(bool buildBlock, Type objectType, int[] index) | |
450 { | |
451 #if FW4 || SILVERLIGHT | |
452 if (buildBlock && _variable != null) | |
453 return _variable; | |
454 #endif | |
455 | |
456 var data = new MappingData | |
457 { | |
458 MappingSchema = Builder.MappingSchema, | |
459 ObjectMapper = Builder.MappingSchema.GetObjectMapper(objectType), | |
460 Index = index | |
461 }; | |
462 | |
463 Expression expr; | |
464 | |
465 if (Builder.DataContextInfo.DataContext == null || | |
466 TypeHelper.IsSameOrParent(typeof(ISupportMapping), objectType) || | |
467 TypeHelper.GetFirstAttribute(objectType, typeof(ObjectFactoryAttribute)) != null) | |
468 { | |
469 expr = Expression.Convert( | |
470 Expression.Call(null, _mapperMethod2, | |
471 ExpressionBuilder.DataReaderParam, | |
472 Expression.Constant(data)), | |
473 objectType); | |
474 } | |
475 else | |
476 { | |
477 expr = Expression.Convert( | |
478 Expression.Call(null, _mapperMethod1, | |
479 ExpressionBuilder.DataReaderParam, | |
480 Expression.Constant(data)), | |
481 objectType); | |
482 } | |
483 | |
484 expr = ProcessExpression(expr); | |
485 | |
486 #if FW4 || SILVERLIGHT | |
487 | |
488 if (!buildBlock) | |
489 return expr; | |
490 | |
491 Builder.BlockVariables. Add(_variable = Expression.Variable(expr.Type, expr.Type.Name + _varIndex++)); | |
492 Builder.BlockExpressions.Add(Expression.Assign(_variable, expr)); | |
493 | |
494 return _variable; | |
495 | |
496 #else | |
497 return expr; | |
498 #endif | |
499 } | |
500 | |
501 protected virtual Expression ProcessExpression(Expression expression) | |
502 { | |
503 return expression; | |
504 } | |
505 | |
506 int[] BuildIndex(int[] index, Type objectType) | |
507 { | |
508 var names = new Dictionary<string,int>(); | |
509 var n = 0; | |
510 | |
511 foreach (MemberMapper mm in Builder.MappingSchema.GetObjectMapper(objectType)) | |
512 if (mm.MapMemberInfo.SqlIgnore == false) | |
513 names.Add(mm.MemberName, n++); | |
514 | |
515 var q = | |
516 from r in SqlTable.Fields.Values.Select((f,i) => new { f, i }) | |
517 where names.ContainsKey(r.f.Name) | |
518 orderby names[r.f.Name] | |
519 select index[r.i]; | |
520 | |
521 return q.ToArray(); | |
522 } | |
523 | |
524 Expression BuildQuery(Type tableType) | |
525 { | |
526 SqlInfo[] info; | |
527 | |
528 if (ObjectType == tableType) | |
529 { | |
530 info = ConvertToIndex(null, 0, ConvertFlags.All); | |
531 } | |
532 else | |
533 { | |
534 info = ConvertToSql(null, 0, ConvertFlags.All); | |
535 | |
536 var table = new SqlTable(Builder.MappingSchema, tableType); | |
537 var q = | |
538 from fld1 in table.Fields.Values.Select((f,i) => new { f, i }) | |
539 join fld2 in info on fld1.f.Name equals ((SqlField)fld2.Sql).Name | |
540 orderby fld1.i | |
541 select GetIndex(fld2); | |
542 | |
543 info = q.ToArray(); | |
544 } | |
545 | |
546 var index = info.Select(idx => ConvertToParentIndex(idx.Index, null)).ToArray(); | |
547 | |
548 if (ObjectType != tableType || InheritanceMapping.Count == 0) | |
549 return BuildTableExpression(!Builder.IsBlockDisable, tableType, index); | |
550 | |
551 Expression expr; | |
552 | |
553 var defaultMapping = InheritanceMapping.SingleOrDefault(m => m.IsDefault); | |
554 | |
555 if (defaultMapping != null) | |
556 { | |
557 expr = Expression.Convert( | |
558 BuildTableExpression(false, defaultMapping.Type, BuildIndex(index, defaultMapping.Type)), | |
559 ObjectType); | |
560 } | |
561 else | |
562 { | |
563 var exceptionMethod = ReflectionHelper.Expressor<object>.MethodExpressor(_ => DefaultInheritanceMappingException(null, null)); | |
564 var dindex = | |
565 ( | |
566 from f in SqlTable.Fields.Values | |
567 where f.Name == InheritanceDiscriminators[0] | |
568 select ConvertToParentIndex(_indexes[f].Index, null) | |
569 ).First(); | |
570 | |
571 expr = Expression.Convert( | |
572 Expression.Call(null, exceptionMethod, | |
573 Expression.Call( | |
574 ExpressionBuilder.DataReaderParam, | |
575 ReflectionHelper.DataReader.GetValue, | |
576 Expression.Constant(dindex)), | |
577 Expression.Constant(ObjectType)), | |
578 ObjectType); | |
579 } | |
580 | |
581 foreach (var mapping in InheritanceMapping.Select((m,i) => new { m, i }).Where(m => m.m != defaultMapping)) | |
582 { | |
583 var dindex = | |
584 ( | |
585 from f in SqlTable.Fields.Values | |
586 where f.Name == InheritanceDiscriminators[mapping.i] | |
587 select ConvertToParentIndex(_indexes[f].Index, null) | |
588 ).First(); | |
589 | |
590 Expression testExpr; | |
591 | |
592 if (mapping.m.Code == null) | |
593 { | |
594 testExpr = Expression.Call( | |
595 ExpressionBuilder.DataReaderParam, | |
596 ReflectionHelper.DataReader.IsDBNull, | |
597 Expression.Constant(dindex)); | |
598 } | |
599 else | |
600 { | |
601 var codeType = mapping.m.Code.GetType(); | |
602 | |
603 testExpr = Expression.Equal( | |
604 Expression.Constant(mapping.m.Code), | |
605 Builder.BuildSql(codeType, dindex)); | |
606 } | |
607 | |
608 expr = Expression.Condition( | |
609 testExpr, | |
610 Expression.Convert(BuildTableExpression(false, mapping.m.Type, BuildIndex(index, mapping.m.Type)), ObjectType), | |
611 expr); | |
612 } | |
613 | |
614 return expr; | |
615 } | |
616 | |
617 public void BuildQuery<T>(Query<T> query, ParameterExpression queryParameter) | |
618 { | |
619 var expr = BuildQuery(typeof(T)); | |
620 var mapper = Builder.BuildMapper<T>(expr); | |
621 | |
622 query.SetQuery(mapper.Compile()); | |
623 } | |
624 | |
625 #endregion | |
626 | |
627 #region BuildExpression | |
628 | |
629 public Expression BuildExpression(Expression expression, int level) | |
630 { | |
631 // Build table. | |
632 // | |
633 var table = FindTable(expression, level, false); | |
634 | |
635 if (table == null) | |
636 { | |
637 if (expression is MemberExpression) | |
638 { | |
639 var memberExpression = (MemberExpression)expression; | |
640 | |
641 if (ObjectMapper != null && | |
642 ObjectMapper.TypeAccessor.OriginalType == memberExpression.Member.DeclaringType) | |
643 { | |
644 throw new LinqException("Member '{0}.{1}' is not a table column.", | |
645 memberExpression.Member.Name, memberExpression.Member.Name); | |
646 } | |
647 } | |
648 | |
649 throw new InvalidOperationException(); | |
650 } | |
651 | |
652 if (table.Field == null) | |
653 return table.Table.BuildQuery(table.Table.OriginalType); | |
654 | |
655 // Build field. | |
656 // | |
657 var info = ConvertToIndex(expression, level, ConvertFlags.Field).Single(); | |
658 var idx = ConvertToParentIndex(info.Index, null); | |
659 | |
660 if (expression is MemberExpression) | |
661 { | |
662 var me = (MemberExpression)expression; | |
663 var memberAccessor = TypeAccessor.GetAccessor(me.Member.DeclaringType)[me.Member.Name]; | |
664 return Builder.BuildSql(memberAccessor, idx); | |
665 } | |
666 else | |
667 { | |
668 return Builder.BuildSql(expression.Type, idx); | |
669 } | |
670 } | |
671 | |
672 #endregion | |
673 | |
674 #region ConvertToSql | |
675 | |
676 public SqlInfo[] ConvertToSql(Expression expression, int level, ConvertFlags flags) | |
677 { | |
678 switch (flags) | |
679 { | |
680 case ConvertFlags.All : | |
681 { | |
682 var table = FindTable(expression, level, false); | |
683 | |
684 if (table.Field == null) | |
685 return table.Table.SqlTable.Fields.Values | |
686 .Select(_ => new SqlInfo(_.MemberMapper.MemberAccessor.MemberInfo) { Sql = _ }) | |
687 .ToArray(); | |
688 | |
689 break; | |
690 } | |
691 | |
692 case ConvertFlags.Key : | |
693 { | |
694 var table = FindTable(expression, level, false); | |
695 | |
696 if (table.Field == null) | |
697 { | |
698 var q = | |
699 from f in table.Table.SqlTable.Fields.Values | |
700 where f.IsPrimaryKey | |
701 orderby f.PrimaryKeyOrder | |
702 select new SqlInfo(f.MemberMapper.MemberAccessor.MemberInfo) { Sql = f }; | |
703 | |
704 var key = q.ToArray(); | |
705 | |
706 return key.Length != 0 ? key : ConvertToSql(expression, level, ConvertFlags.All); | |
707 } | |
708 | |
709 break; | |
710 } | |
711 | |
712 case ConvertFlags.Field : | |
713 { | |
714 var table = FindTable(expression, level, true); | |
715 | |
716 if (table.Field != null) | |
717 return new[] | |
718 { | |
719 new SqlInfo(table.Field.MemberMapper.MemberAccessor.MemberInfo) { Sql = table.Field } | |
720 }; | |
721 | |
722 break; | |
723 } | |
724 } | |
725 | |
726 throw new InvalidOperationException(); | |
727 } | |
728 | |
729 #endregion | |
730 | |
731 #region ConvertToIndex | |
732 | |
733 readonly Dictionary<ISqlExpression,SqlInfo> _indexes = new Dictionary<ISqlExpression,SqlInfo>(); | |
734 | |
735 protected SqlInfo GetIndex(SqlInfo expr) | |
736 { | |
737 SqlInfo n; | |
738 | |
739 if (_indexes.TryGetValue(expr.Sql, out n)) | |
740 return n; | |
741 | |
742 if (expr.Sql is SqlField) | |
743 { | |
744 var field = (SqlField)expr.Sql; | |
745 expr.Index = SqlQuery.Select.Add(field, field.Alias); | |
746 } | |
747 else | |
748 { | |
749 expr.Index = SqlQuery.Select.Add(expr.Sql); | |
750 } | |
751 | |
752 expr.Query = SqlQuery; | |
753 | |
754 _indexes.Add(expr.Sql, expr); | |
755 | |
756 return expr; | |
757 } | |
758 | |
759 public SqlInfo[] ConvertToIndex(Expression expression, int level, ConvertFlags flags) | |
760 { | |
761 switch (flags) | |
762 { | |
763 case ConvertFlags.Field : | |
764 case ConvertFlags.Key : | |
765 case ConvertFlags.All : | |
766 | |
767 var info = ConvertToSql(expression, level, flags); | |
768 | |
769 for (var i = 0; i < info.Length; i++) | |
770 info[i] = GetIndex(info[i]); | |
771 | |
772 return info; | |
773 } | |
774 | |
775 throw new InvalidOperationException(); | |
776 } | |
777 | |
778 #endregion | |
779 | |
780 #region IsExpression | |
781 | |
782 public IsExpressionResult IsExpression(Expression expression, int level, RequestFor requestFor) | |
783 { | |
784 switch (requestFor) | |
785 { | |
786 case RequestFor.Field : | |
787 { | |
788 var table = FindTable(expression, level, false); | |
789 return new IsExpressionResult(table != null && table.Field != null); | |
790 } | |
791 | |
792 case RequestFor.Table : | |
793 case RequestFor.Object : | |
794 { | |
795 var table = FindTable(expression, level, false); | |
796 var isTable = | |
797 table != null && | |
798 table.Field == null && | |
799 (expression == null || expression.GetLevelExpression(table.Level) == expression); | |
800 | |
801 return new IsExpressionResult(isTable, isTable ? table.Table : null); | |
802 } | |
803 | |
804 case RequestFor.Expression : | |
805 { | |
806 if (expression == null) | |
807 return IsExpressionResult.False; | |
808 | |
809 var levelExpression = expression.GetLevelExpression(level); | |
810 | |
811 switch (levelExpression.NodeType) | |
812 { | |
813 case ExpressionType.MemberAccess : | |
814 case ExpressionType.Parameter : | |
815 case ExpressionType.Call : | |
816 | |
817 var table = FindTable(expression, level, false); | |
818 return new IsExpressionResult(table == null); | |
819 } | |
820 | |
821 return IsExpressionResult.True; | |
822 } | |
823 | |
824 case RequestFor.Association : | |
825 { | |
826 if (ObjectMapper.Associations.Count > 0) | |
827 { | |
828 var table = FindTable(expression, level, false); | |
829 var isat = | |
830 table != null && | |
831 table.Table is AssociatedTableContext && | |
832 table.Field == null && | |
833 (expression == null || expression.GetLevelExpression(table.Level) == expression); | |
834 | |
835 return new IsExpressionResult(isat, isat ? table.Table : null); | |
836 } | |
837 | |
838 return IsExpressionResult.False; | |
839 } | |
840 } | |
841 | |
842 return IsExpressionResult.False; | |
843 } | |
844 | |
845 #endregion | |
846 | |
847 #region GetContext | |
848 | |
849 interface IAssociationHelper | |
850 { | |
851 Expression GetExpression(Expression parent, AssociatedTableContext association); | |
852 } | |
853 | |
854 class AssociationHelper<T> : IAssociationHelper | |
855 where T : class | |
856 { | |
857 public Expression GetExpression(Expression parent, AssociatedTableContext association) | |
858 { | |
859 Expression expr = null; | |
860 var param = Expression.Parameter(typeof(T), "c"); | |
861 | |
862 foreach (var cond in (association).ParentAssociationJoin.Condition.Conditions) | |
863 { | |
864 var p = (SqlQuery.Predicate.ExprExpr)cond.Predicate; | |
865 var e1 = Expression.MakeMemberAccess(parent, ((SqlField)p.Expr1).MemberMapper.MemberAccessor.MemberInfo); | |
866 var e2 = Expression.MakeMemberAccess(param, ((SqlField)p.Expr2).MemberMapper.MemberAccessor.MemberInfo) as Expression; | |
867 | |
868 while (e1.Type != e2.Type) | |
869 { | |
870 if (TypeHelper.IsNullableType(e1.Type)) | |
871 { | |
872 e1 = Expression.PropertyOrField(e1, "Value"); | |
873 continue; | |
874 } | |
875 | |
876 if (TypeHelper.IsNullableType(e2.Type)) | |
877 { | |
878 e2 = Expression.PropertyOrField(e2, "Value"); | |
879 continue; | |
880 } | |
881 | |
882 e2 = Expression.Convert(e2, e1.Type); | |
883 } | |
884 | |
885 var ex = Expression.Equal(e1, e2); | |
886 | |
887 expr = expr == null ? ex : Expression.AndAlso(expr, ex); | |
888 } | |
889 | |
890 var predicate = Expression.Lambda<Func<T,bool>>(expr, param); | |
891 | |
892 return association.Builder.DataContextInfo.DataContext.GetTable<T>().Where(predicate).Expression; | |
893 } | |
894 } | |
895 | |
896 public IBuildContext GetContext(Expression expression, int level, BuildInfo buildInfo) | |
897 { | |
898 if (expression == null) | |
899 { | |
900 if (buildInfo != null && buildInfo.IsSubQuery) | |
901 { | |
902 var table = new TableContext( | |
903 Builder, | |
904 new BuildInfo(Parent is SelectManyBuilder.SelectManyContext ? this : Parent, Expression, buildInfo.SqlQuery), | |
905 SqlTable.ObjectType); | |
906 | |
907 return table; | |
908 } | |
909 | |
910 return this; | |
911 } | |
912 | |
913 if (ObjectMapper.Associations.Count > 0) | |
914 { | |
915 var levelExpression = expression.GetLevelExpression(level); | |
916 | |
917 if (buildInfo != null && buildInfo.IsSubQuery) | |
918 { | |
919 if (levelExpression == expression && expression.NodeType == ExpressionType.MemberAccess) | |
920 { | |
921 var tableLevel = GetAssociation(expression, level); | |
922 var association = (AssociatedTableContext)tableLevel.Table; | |
923 | |
924 if (association.IsList) | |
925 { | |
926 var ma = (MemberExpression)buildInfo.Expression; | |
927 var atype = typeof(AssociationHelper<>).MakeGenericType(association.ObjectType); | |
928 var helper = (IAssociationHelper)Activator.CreateInstance(atype); | |
929 var expr = helper.GetExpression(ma.Expression, association); | |
930 | |
931 buildInfo.IsAssociationBuilt = true; | |
932 | |
933 if (tableLevel.IsNew || buildInfo.CopyTable) | |
934 association.ParentAssociationJoin.IsWeak = true; | |
935 | |
936 return Builder.BuildSequence(new BuildInfo(buildInfo, expr)); | |
937 } | |
938 } | |
939 else | |
940 { | |
941 var association = GetAssociation(levelExpression, level); | |
942 var paj = ((AssociatedTableContext)association.Table).ParentAssociationJoin; | |
943 | |
944 paj.IsWeak = paj.IsWeak && buildInfo.CopyTable; | |
945 | |
946 return association.Table.GetContext(expression, level + 1, buildInfo); | |
947 } | |
948 } | |
949 } | |
950 | |
951 throw new InvalidOperationException(); | |
952 } | |
953 | |
954 #endregion | |
955 | |
956 #region ConvertToParentIndex | |
957 | |
958 public int ConvertToParentIndex(int index, IBuildContext context) | |
959 { | |
960 return Parent == null ? index : Parent.ConvertToParentIndex(index, this); | |
961 } | |
962 | |
963 #endregion | |
964 | |
965 #region SetAlias | |
966 | |
967 public void SetAlias(string alias) | |
968 { | |
969 if (alias.Contains('<')) | |
970 return; | |
971 | |
972 if (SqlTable.Alias == null) | |
973 SqlTable.Alias = alias; | |
974 } | |
975 | |
976 #endregion | |
977 | |
978 #region GetSubQuery | |
979 | |
980 public ISqlExpression GetSubQuery(IBuildContext context) | |
981 { | |
982 return null; | |
983 } | |
984 | |
985 #endregion | |
986 | |
987 #region Helpers | |
988 | |
989 SqlField GetField(Expression expression, int level, bool throwException) | |
990 { | |
991 if (expression.NodeType == ExpressionType.MemberAccess) | |
992 { | |
993 var memberExpression = (MemberExpression)expression; | |
994 var levelExpression = expression.GetLevelExpression(level); | |
995 | |
996 if (levelExpression.NodeType == ExpressionType.MemberAccess) | |
997 { | |
998 if (levelExpression != expression) | |
999 { | |
1000 var levelMember = (MemberExpression)levelExpression; | |
1001 | |
1002 if (TypeHelper.IsNullableValueMember(memberExpression.Member) && memberExpression.Expression == levelExpression) | |
1003 memberExpression = levelMember; | |
1004 else | |
1005 { | |
1006 var sameType = | |
1007 levelMember.Member.ReflectedType == SqlTable.ObjectType || | |
1008 levelMember.Member.DeclaringType == SqlTable.ObjectType; | |
1009 | |
1010 if (!sameType) | |
1011 { | |
1012 var mi = SqlTable.ObjectType.GetMember(levelMember.Member.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); | |
1013 sameType = mi.Any(_ => _.DeclaringType == levelMember.Member.DeclaringType); | |
1014 } | |
1015 | |
1016 if (sameType || InheritanceMapping.Count > 0) | |
1017 { | |
1018 foreach (var field in SqlTable.Fields.Values) | |
1019 { | |
1020 if (field.MemberMapper is MemberMapper.ComplexMapper) | |
1021 { | |
1022 var name = levelMember.Member.Name; | |
1023 | |
1024 for (var ex = (MemberExpression)expression; ex != levelMember; ex = (MemberExpression)ex.Expression) | |
1025 name += "." + ex.Member.Name; | |
1026 | |
1027 if (field.MemberMapper.MemberName == name) | |
1028 return field; | |
1029 } | |
1030 } | |
1031 } | |
1032 } | |
1033 } | |
1034 | |
1035 if (levelExpression == memberExpression) | |
1036 { | |
1037 foreach (var field in SqlTable.Fields.Values) | |
1038 { | |
1039 if (TypeHelper.Equals(field.MemberMapper.MapMemberInfo.MemberAccessor.MemberInfo, memberExpression.Member, SqlTable.ObjectType)) | |
1040 { | |
1041 if (field.MemberMapper is MemberMapper.ComplexMapper && | |
1042 field.MemberMapper.MemberName.IndexOf('.') > 0) | |
1043 { | |
1044 var name = memberExpression.Member.Name; | |
1045 var me = memberExpression; | |
1046 | |
1047 if (!(me.Expression is MemberExpression)) | |
1048 return null; | |
1049 | |
1050 while (me.Expression is MemberExpression) | |
1051 { | |
1052 me = (MemberExpression)me.Expression; | |
1053 name = me.Member.Name + '.' + name; | |
1054 } | |
1055 | |
1056 return SqlTable.Fields.Values.FirstOrDefault(f => f.MemberMapper.MemberName == name); | |
1057 } | |
1058 | |
1059 return field; | |
1060 } | |
1061 | |
1062 if (InheritanceMapping.Count > 0 && field.Name == memberExpression.Member.Name) | |
1063 foreach (var mapping in InheritanceMapping) | |
1064 foreach (MemberMapper mm in Builder.MappingSchema.GetObjectMapper(mapping.Type)) | |
1065 if (TypeHelper.Equals(mm.MapMemberInfo.MemberAccessor.MemberInfo, memberExpression.Member)) | |
1066 return field; | |
1067 } | |
1068 | |
1069 if (throwException && | |
1070 ObjectMapper != null && | |
1071 ObjectMapper.TypeAccessor.OriginalType == memberExpression.Member.DeclaringType) | |
1072 { | |
1073 throw new LinqException("Member '{0}.{1}' is not a table column.", | |
1074 memberExpression.Member.DeclaringType.Name, memberExpression.Member.Name); | |
1075 } | |
1076 } | |
1077 } | |
1078 } | |
1079 | |
1080 return null; | |
1081 } | |
1082 | |
1083 [JetBrains.Annotations.NotNull] | |
1084 readonly Dictionary<MemberInfo,AssociatedTableContext> _associations = | |
1085 new Dictionary<MemberInfo,AssociatedTableContext>(new MemberInfoComparer()); | |
1086 | |
1087 class TableLevel | |
1088 { | |
1089 public TableContext Table; | |
1090 public SqlField Field; | |
1091 public int Level; | |
1092 public bool IsNew; | |
1093 } | |
1094 | |
1095 TableLevel FindTable(Expression expression, int level, bool throwException) | |
1096 { | |
1097 if (expression == null) | |
1098 return new TableLevel { Table = this }; | |
1099 | |
1100 var levelExpression = expression.GetLevelExpression(level); | |
1101 | |
1102 switch (levelExpression.NodeType) | |
1103 { | |
1104 case ExpressionType.MemberAccess : | |
1105 case ExpressionType.Parameter : | |
1106 { | |
1107 var field = GetField(expression, level, throwException); | |
1108 | |
1109 if (field != null || (level == 0 && levelExpression == expression)) | |
1110 return new TableLevel { Table = this, Field = field, Level = level }; | |
1111 | |
1112 return GetAssociation(expression, level); | |
1113 } | |
1114 } | |
1115 | |
1116 return null; | |
1117 } | |
1118 | |
1119 TableLevel GetAssociation(Expression expression, int level) | |
1120 { | |
1121 var objectMapper = ObjectMapper; | |
1122 var levelExpression = expression.GetLevelExpression(level); | |
1123 var inheritance = | |
1124 ( | |
1125 from m in InheritanceMapping | |
1126 let om = Builder.MappingSchema.GetObjectMapper(m.Type) | |
1127 where om.Associations.Count > 0 | |
1128 select om | |
1129 ).ToList(); | |
1130 | |
1131 if (objectMapper.Associations.Count > 0 || inheritance.Count > 0) | |
1132 { | |
1133 if (levelExpression.NodeType == ExpressionType.MemberAccess) | |
1134 { | |
1135 var memberExpression = (MemberExpression)levelExpression; | |
1136 var isNew = false; | |
1137 | |
1138 AssociatedTableContext tableAssociation; | |
1139 | |
1140 if (!_associations.TryGetValue(memberExpression.Member, out tableAssociation)) | |
1141 { | |
1142 var q = | |
1143 from a in objectMapper.Associations.Concat(inheritance.SelectMany(om => om.Associations)) | |
1144 where TypeHelper.Equals(a.MemberAccessor.MemberInfo, memberExpression.Member) | |
1145 select new AssociatedTableContext(Builder, this, a) { Parent = Parent }; | |
1146 | |
1147 tableAssociation = q.FirstOrDefault(); | |
1148 | |
1149 isNew = true; | |
1150 | |
1151 _associations.Add(memberExpression.Member, tableAssociation); | |
1152 } | |
1153 | |
1154 if (tableAssociation != null) | |
1155 { | |
1156 if (levelExpression == expression) | |
1157 return new TableLevel { Table = tableAssociation, Level = level, IsNew = isNew }; | |
1158 | |
1159 var al = tableAssociation.GetAssociation(expression, level + 1); | |
1160 | |
1161 if (al != null) | |
1162 return al; | |
1163 | |
1164 var field = tableAssociation.GetField(expression, level + 1, false); | |
1165 | |
1166 return new TableLevel { Table = tableAssociation, Field = field, Level = field == null ? level : level + 1, IsNew = isNew }; | |
1167 } | |
1168 } | |
1169 } | |
1170 | |
1171 return null; | |
1172 } | |
1173 | |
1174 #endregion | |
1175 } | |
1176 | |
1177 #endregion | |
1178 | |
1179 #region AssociatedTableContext | |
1180 | |
1181 public class AssociatedTableContext : TableContext | |
1182 { | |
1183 public readonly TableContext ParentAssociation; | |
1184 public readonly SqlQuery.JoinedTable ParentAssociationJoin; | |
1185 public readonly Association Association; | |
1186 public readonly bool IsList; | |
1187 | |
1188 public override IBuildContext Parent | |
1189 { | |
1190 get { return ParentAssociation.Parent; } | |
1191 set { } | |
1192 } | |
1193 | |
1194 public AssociatedTableContext(ExpressionBuilder builder, TableContext parent, Association association) | |
1195 : base(builder, parent.SqlQuery) | |
1196 { | |
1197 var type = TypeHelper.GetMemberType(association.MemberAccessor.MemberInfo); | |
1198 var left = association.CanBeNull; | |
1199 | |
1200 if (TypeHelper.IsSameOrParent(typeof(IEnumerable), type)) | |
1201 { | |
1202 var etypes = TypeHelper.GetGenericArguments(type, typeof(IEnumerable)); | |
1203 type = etypes != null && etypes.Length > 0 ? etypes[0] : TypeHelper.GetListItemType(type); | |
1204 IsList = true; | |
1205 } | |
1206 | |
1207 OriginalType = type; | |
1208 ObjectType = GetObjectType(); | |
1209 ObjectMapper = Builder.MappingSchema.GetObjectMapper(ObjectType); | |
1210 SqlTable = new SqlTable(builder.MappingSchema, ObjectType); | |
1211 | |
1212 var psrc = parent.SqlQuery.From[parent.SqlTable]; | |
1213 var join = left ? SqlTable.WeakLeftJoin() : IsList ? SqlTable.InnerJoin() : SqlTable.WeakInnerJoin(); | |
1214 | |
1215 Association = association; | |
1216 ParentAssociation = parent; | |
1217 ParentAssociationJoin = join.JoinedTable; | |
1218 | |
1219 psrc.Joins.Add(join.JoinedTable); | |
1220 | |
1221 for (var i = 0; i < association.ThisKey.Length; i++) | |
1222 { | |
1223 SqlField field1; | |
1224 SqlField field2; | |
1225 | |
1226 if (!parent.SqlTable.Fields.TryGetValue(association.ThisKey[i], out field1)) | |
1227 throw new LinqException("Association key '{0}' not found for type '{1}.", association.ThisKey[i], parent.ObjectType); | |
1228 | |
1229 if (!SqlTable.Fields.TryGetValue(association.OtherKey[i], out field2)) | |
1230 throw new LinqException("Association key '{0}' not found for type '{1}.", association.OtherKey[i], ObjectType); | |
1231 | |
1232 join.Field(field1).Equal.Field(field2); | |
1233 } | |
1234 | |
1235 Init(); | |
1236 } | |
1237 | |
1238 protected override Expression ProcessExpression(Expression expression) | |
1239 { | |
1240 var isLeft = false; | |
1241 | |
1242 for ( | |
1243 var association = this; | |
1244 isLeft == false && association != null; | |
1245 association = association.ParentAssociation as AssociatedTableContext) | |
1246 { | |
1247 isLeft = | |
1248 association.ParentAssociationJoin.JoinType == SqlQuery.JoinType.Left || | |
1249 association.ParentAssociationJoin.JoinType == SqlQuery.JoinType.OuterApply; | |
1250 } | |
1251 | |
1252 if (isLeft) | |
1253 { | |
1254 Expression cond = null; | |
1255 | |
1256 var keys = ConvertToIndex(null, 0, ConvertFlags.Key); | |
1257 | |
1258 foreach (var key in keys) | |
1259 { | |
1260 var index2 = ConvertToParentIndex(key.Index, null); | |
1261 | |
1262 Expression e = Expression.Call( | |
1263 ExpressionBuilder.DataReaderParam, | |
1264 ReflectionHelper.DataReader.IsDBNull, | |
1265 Expression.Constant(index2)); | |
1266 | |
1267 cond = cond == null ? e : Expression.AndAlso(cond, e); | |
1268 } | |
1269 | |
1270 expression = Expression.Condition(cond, Expression.Constant(null, expression.Type), expression); | |
1271 } | |
1272 | |
1273 return expression; | |
1274 } | |
1275 } | |
1276 | |
1277 #endregion | |
1278 } | |
1279 } |