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,
|
|
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 }
|