1 //------------------------------------------------------------------------------
2 // <copyright file="Translator.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
8 
9 namespace System.Data.Common.Internal.Materialization
10 {
11     using System.Collections.Generic;
12     using System.Data;
13     using System.Data.Common.QueryCache;
14     using System.Data.Common.Utils;
15     using System.Data.Entity;
16     using System.Data.Mapping;
17     using System.Data.Metadata.Edm;
18     using System.Data.Objects;
19     using System.Data.Objects.DataClasses;
20     using System.Data.Objects.ELinq;
21     using System.Data.Objects.Internal;
22     using System.Data.Query.InternalTrees;
23     using System.Diagnostics;
24     using System.Globalization;
25     using System.Linq.Expressions;
26     using System.Reflection;
27     using System.Runtime.CompilerServices;
28     using System.Security.Permissions;
29 
30     /// <summary>
31     /// Struct containing the requested type and parent column map used
32     /// as the arg in the Translator visitor.
33     /// </summary>
34     internal struct TranslatorArg
35     {
36         internal readonly Type RequestedType;
37 
TranslatorArgSystem.Data.Common.Internal.Materialization.TranslatorArg38         internal TranslatorArg(Type requestedType)
39         {
40             this.RequestedType = requestedType;
41         }
42     }
43 
44     /// <summary>
45     /// Type returned by the Translator visitor; allows us to put the logic
46     /// to ensure a specific return type in a single place, instead of in
47     /// each Visit method.
48     /// </summary>
49     internal class TranslatorResult
50     {
51         private readonly Expression ReturnedExpression;
52         private readonly Type RequestedType;
53 
TranslatorResult(Expression returnedExpression, Type requestedType)54         internal TranslatorResult(Expression returnedExpression, Type requestedType)
55         {
56             this.RequestedType = requestedType;
57             this.ReturnedExpression = returnedExpression;
58         }
59 
60         /// <summary>
61         /// Return the expression; wrapped with the appropriate cast/convert
62         /// logic to guarantee it's type.
63         /// </summary>
64         internal Expression Expression
65         {
66             get
67             {
68                 Expression result = Translator.Emit_EnsureType(ReturnedExpression, RequestedType);
69                 return result;
70             }
71         }
72 
73         /// <summary>
74         /// Return the expression without attempting to cast/convert to the requested type.
75         /// </summary>
76         internal Expression UnconvertedExpression
77         {
78             get
79             {
80                 return ReturnedExpression;
81             }
82         }
83 
84         /// <summary>
85         /// Checks if the expression represents an wrapped entity and if so creates an expression
86         /// that extracts the raw entity from the wrapper.
87         /// </summary>
88         internal Expression UnwrappedExpression
89         {
90             get
91             {
92                 if (!typeof(IEntityWrapper).IsAssignableFrom(ReturnedExpression.Type))
93                 {
94                     return ReturnedExpression;
95                 }
96                 return Translator.Emit_UnwrapAndEnsureType(ReturnedExpression, RequestedType);
97             }
98         }
99     }
100 
101     /// <summary>
102     /// For collection results, we really want to know the expression to
103     /// get the coordinator from its stateslot as well, so we have an
104     /// additional one...
105     /// </summary>
106     internal class CollectionTranslatorResult : TranslatorResult
107     {
108         internal readonly Expression ExpressionToGetCoordinator;
109 
CollectionTranslatorResult(Expression returnedExpression, ColumnMap columnMap, Type requestedType, Expression expressionToGetCoordinator)110         internal CollectionTranslatorResult(Expression returnedExpression, ColumnMap columnMap, Type requestedType, Expression expressionToGetCoordinator)
111             : base(returnedExpression, requestedType)
112         {
113             this.ExpressionToGetCoordinator = expressionToGetCoordinator;
114         }
115     }
116 
117     /// <summary>
118     /// Translates query ColumnMap into ShaperFactory. Basically, we interpret the
119     /// ColumnMap and compile delegates used to materialize results.
120     /// </summary>
121     internal class Translator : ColumnMapVisitorWithResults<TranslatorResult, TranslatorArg>
122     {
123         #region private state
124 
125         /// <summary>
126         /// Gets the O-Space Metadata workspace.
127         /// </summary>
128         private readonly MetadataWorkspace _workspace;
129 
130         /// <summary>
131         /// Gets structure telling us how to interpret 'span' rows (includes implicit
132         /// relationship span and explicit full span via ObjectQuery.Include().
133         /// </summary>
134         private readonly SpanIndex _spanIndex;
135 
136         /// <summary>
137         /// Gets the MergeOption for the current query (influences our handling of
138         /// entities when they are materialized).
139         /// </summary>
140         private readonly MergeOption _mergeOption;
141 
142         /// <summary>
143         /// When true, indicates we're processing for the value layer (BridgeDataReader)
144         /// and not the ObjectMaterializer
145         /// </summary>
146         private readonly bool IsValueLayer;
147 
148         /// <summary>
149         /// Gets scratchpad for topmost nested reader coordinator.
150         /// </summary>
151         private CoordinatorScratchpad _rootCoordinatorScratchpad;
152 
153         /// <summary>
154         /// Gets scratchpad for the coordinator builder for the nested reader currently
155         /// being translated or emitted.
156         /// </summary>
157         private CoordinatorScratchpad _currentCoordinatorScratchpad;
158 
159         /// <summary>
160         /// Gets number of 'Shaper.State' slots allocated (used to hold onto intermediate
161         /// values during materialization)
162         /// </summary>
163         private int _stateSlotCount;
164 
165         /// <summary>
166         /// Set to true if any Entity/Complex type/property for which we're emitting a
167         /// handler is non-public. Used to determine which security checks are necessary
168         /// when invoking the delegate.
169         /// </summary>
170         private bool _hasNonPublicMembers;
171 
172         /// <summary>
173         /// Local cache of ObjectTypeMappings for EdmTypes (to prevent expensive lookups).
174         /// </summary>
175         private readonly Dictionary<EdmType, ObjectTypeMapping> _objectTypeMappings = new Dictionary<EdmType, ObjectTypeMapping>();
176 
177         #endregion
178 
179         #region constructor
180 
Translator(MetadataWorkspace workspace, SpanIndex spanIndex, MergeOption mergeOption, bool valueLayer)181         private Translator(MetadataWorkspace workspace, SpanIndex spanIndex, MergeOption mergeOption, bool valueLayer)
182         {
183             _workspace = workspace;
184             _spanIndex = spanIndex;
185             _mergeOption = mergeOption;
186             IsValueLayer = valueLayer;
187         }
188 
189         #endregion
190 
191         #region "public" surface area
192 
193         /// <summary>
194         /// The main entry point for the translation process. Given a ColumnMap, returns
195         /// a ShaperFactory which can be used to materialize results for a query.
196         /// </summary>
TranslateColumnMap(QueryCacheManager queryCacheManager, ColumnMap columnMap, MetadataWorkspace workspace, SpanIndex spanIndex, MergeOption mergeOption, bool valueLayer)197         internal static ShaperFactory<TRequestedType> TranslateColumnMap<TRequestedType>(QueryCacheManager queryCacheManager, ColumnMap columnMap, MetadataWorkspace workspace, SpanIndex spanIndex, MergeOption mergeOption, bool valueLayer)
198         {
199             Debug.Assert(columnMap is CollectionColumnMap, "root column map must be a collection for a query");
200 
201             // If the query cache already contains a plan, then we're done
202             ShaperFactory<TRequestedType> result;
203             string columnMapKey = ColumnMapKeyBuilder.GetColumnMapKey(columnMap, spanIndex);
204             ShaperFactoryQueryCacheKey<TRequestedType> cacheKey = new ShaperFactoryQueryCacheKey<TRequestedType>(columnMapKey, mergeOption, valueLayer);
205 
206             if (queryCacheManager.TryCacheLookup<ShaperFactoryQueryCacheKey<TRequestedType>, ShaperFactory<TRequestedType>>(cacheKey, out result))
207             {
208                 return result;
209             }
210 
211             // Didn't find it in the cache, so we have to do the translation;  First create
212             // the translator visitor that recursively tranforms ColumnMaps into Expressions
213             // stored on the CoordinatorScratchpads it also constructs.  We'll compile those
214             // expressions into delegates later.
215             Translator translator = new Translator(workspace, spanIndex, mergeOption, valueLayer);
216             columnMap.Accept(translator, new TranslatorArg(typeof(IEnumerable<>).MakeGenericType(typeof(TRequestedType))));
217 
218             Debug.Assert(null != translator._rootCoordinatorScratchpad, "translating the root of the query must populate _rootCoordinatorBuilder"); // how can this happen?
219 
220             // We're good. Go ahead and recursively compile the CoordinatorScratchpads we
221             // created in the vistor into CoordinatorFactories which contain compiled
222             // delegates for the expressions we generated.
223             CoordinatorFactory<TRequestedType> coordinatorFactory = (CoordinatorFactory<TRequestedType>)translator._rootCoordinatorScratchpad.Compile();
224 
225             // Along the way we constructed a nice delegate to perform runtime permission
226             // checks (e.g. for LinkDemand and non-public members).  We need that now.
227             Action checkPermissionsDelegate = translator.GetCheckPermissionsDelegate();
228 
229             // Finally, take everything we've produced, and create the ShaperFactory to
230             // contain it all, then add it to the query cache so we don't need to do this
231             // for this query again.
232             result = new ShaperFactory<TRequestedType>(translator._stateSlotCount, coordinatorFactory, checkPermissionsDelegate, mergeOption);
233             QueryCacheEntry cacheEntry = new QueryCacheEntry(cacheKey, result);
234             if (queryCacheManager.TryLookupAndAdd(cacheEntry, out cacheEntry))
235             {
236                 // Someone beat us to it. Use their result instead.
237                 result = (ShaperFactory<TRequestedType>)cacheEntry.GetTarget();
238             }
239             return result;
240         }
241 
242         /// <summary>
243         /// Compiles a delegate taking a Shaper instance and returning values. Used to compile
244         /// Expressions produced by the emitter.
245         ///
246         /// Asserts MemberAccess to skip visbility check.
247         /// This means that that security checks are skipped. Before calling this
248         /// method you must ensure that you've done a TestComple on expressions provided
249         /// by the user to ensure the compilation doesn't violate them.
250         /// </summary>
251         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2128")]
252         [System.Security.SecuritySafeCritical]
253         [ReflectionPermission(SecurityAction.Assert, MemberAccess = true)]
Compile(Expression body)254         internal static Func<Shaper, TResult> Compile<TResult>(Expression body)
255         {
256             var lambda = Expression.Lambda<Func<Shaper, TResult>>(body, Shaper_Parameter);
257             return lambda.Compile();
258         }
259 
260         /// <summary>
261         /// Non-generic version of Compile (where the result type is passed in as an argument rather
262         /// than a type parameter)
263         /// </summary>
264         [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
Compile(Type resultType, Expression body)265         internal static object Compile(Type resultType, Expression body)
266         {
267             MethodInfo compile = Translator_Compile.MakeGenericMethod(resultType);
268             return compile.Invoke(null, new object[] { body });
269         }
270 
271         #endregion
272 
273         #region helpers
274 
275         /// <summary>
276         /// Allocates a slot in 'Shaper.State' which can be used as storage for
277         /// materialization tasks (e.g. remembering key values for a nested collection)
278         /// </summary>
AllocateStateSlot()279         private int AllocateStateSlot()
280         {
281             return _stateSlotCount++;
282         }
283 
284         /// <summary>
285         /// Returns a delegate performing necessary permission checks identified
286         /// by this translator.  This delegate must be called every time a row is
287         /// read from the ObjectResult enumerator, since the enumerator can be
288         /// passed across security contexts.
289         /// </summary>
GetCheckPermissionsDelegate()290         private Action GetCheckPermissionsDelegate()
291         {
292             // Emit an action to check runtime permissions.
293             return _hasNonPublicMembers ? (Action)DemandMemberAccess : null;
294         }
295 
DemandMemberAccess()296         private static void DemandMemberAccess()
297         {
298             LightweightCodeGenerator.MemberAccessReflectionPermission.Demand();
299         }
300 
301         /// <summary>
302         /// Try compiling the user expressions to ensure it would succeed without an
303         /// assert (user expressions are inlined with references to EF internals which
304         /// require the assert so we need to check the user expressions separately).
305         ///
306         /// This method is called every time a new query result is returned to make sure
307         /// the user expressions can be compiled in the current security context.
308         /// </summary>
VerifyUserExpressions(IEnumerable<Expression<Func<object>>> userExpressions)309         private static void VerifyUserExpressions(IEnumerable<Expression<Func<object>>> userExpressions)
310         {
311             // As an optimization, check if we have member access permission. If so,
312             // we know the compile would succeed and don't need to make the effort.
313             if (!LightweightCodeGenerator.HasMemberAccessReflectionPermission())
314             {
315                 // If we don't have MemberAccess, compile the expressions to see if they
316                 // might be satisfied by RestrictedMemberAccess.
317                 foreach (Expression<Func<object>> userExpression in userExpressions)
318                 {
319                     userExpression.Compile();
320                 }
321             }
322         }
323 
324         /// <summary>
325         /// Return the CLR type we're supposed to materialize for the TypeUsage
326         /// </summary>
DetermineClrType(TypeUsage typeUsage)327         private Type DetermineClrType(TypeUsage typeUsage)
328         {
329             return DetermineClrType(typeUsage.EdmType);
330         }
331 
332         /// <summary>
333         /// Return the CLR type we're supposed to materialize for the EdmType
334         /// </summary>
DetermineClrType(EdmType edmType)335         private Type DetermineClrType(EdmType edmType)
336         {
337             Type result = null;
338             // Normalize for spandex
339             edmType = ResolveSpanType(edmType);
340 
341             switch (edmType.BuiltInTypeKind)
342             {
343                 case BuiltInTypeKind.EntityType:
344                 case BuiltInTypeKind.ComplexType:
345                     if (IsValueLayer)
346                     {
347                         result = typeof(RecordState);
348                     }
349                     else
350                     {
351                         result = LookupObjectMapping(edmType).ClrType.ClrType;
352                     }
353                     break;
354 
355                 case BuiltInTypeKind.RefType:
356                     result = typeof(EntityKey);
357                     break;
358 
359                 case BuiltInTypeKind.CollectionType:
360                     if (IsValueLayer)
361                     {
362                         result = typeof(Coordinator<RecordState>);
363                     }
364                     else
365                     {
366                         EdmType edmElementType = ((CollectionType)edmType).TypeUsage.EdmType;
367                         result = DetermineClrType(edmElementType);
368                         result = typeof(IEnumerable<>).MakeGenericType(result);
369                     }
370                     break;
371 
372                 case BuiltInTypeKind.EnumType:
373                     if (IsValueLayer)
374                     {
375                         result = DetermineClrType(((EnumType)edmType).UnderlyingType);
376                     }
377                     else
378                     {
379                         result = LookupObjectMapping(edmType).ClrType.ClrType;
380                         result = typeof(Nullable<>).MakeGenericType(result);
381                     }
382                     break;
383 
384                 case BuiltInTypeKind.PrimitiveType:
385                     result = ((PrimitiveType)edmType).ClrEquivalentType;
386                     if (result.IsValueType)
387                     {
388                         result = typeof(Nullable<>).MakeGenericType(result);
389                     }
390                     break;
391 
392                 case BuiltInTypeKind.RowType:
393                     if (IsValueLayer)
394                     {
395                         result = typeof(RecordState);
396                     }
397                     else
398                     {
399                         // LINQ has anonymous types that aren't going to show up in our
400                         // metadata workspace, and we don't want to hydrate a record when
401                         // we need an anonymous type.  ELINQ solves this by annotating the
402                         // edmType with some additional information, which we'll pick up
403                         // here.
404                         InitializerMetadata initializerMetadata = ((RowType)edmType).InitializerMetadata;
405                         if (null != initializerMetadata)
406                         {
407                             result = initializerMetadata.ClrType;
408                         }
409                         else
410                         {
411                             // Otherwise, by default, we'll give DbDataRecord results (the
412                             // user can also cast to IExtendedDataRecord)
413                             result = typeof(DbDataRecord);
414                         }
415                     }
416                     break;
417 
418                 default:
419                     Debug.Fail(string.Format(CultureInfo.CurrentCulture, "The type {0} was not the expected scalar, enumeration, collection, structural, nominal, or reference type.", edmType.GetType()));
420                     break;
421             }
422             Debug.Assert(null != result, "no result?"); // just making sure we cover this in the switch statement.
423 
424             return result;
425         }
426 
427         /// <summary>
428         /// Get the ConstructorInfo for the type specified, and ensure we keep track
429         /// of any security requirements that the type has.
430         /// </summary>
GetConstructor(Type type)431         private ConstructorInfo GetConstructor(Type type)
432         {
433             ConstructorInfo result = null;
434             if (!type.IsAbstract)
435             {
436                 result = LightweightCodeGenerator.GetConstructorForType(type);
437 
438                 // remember security requirements for this constructor
439                 if (!LightweightCodeGenerator.IsPublic(result))
440                 {
441                     _hasNonPublicMembers = true;
442                 }
443             }
444             return result;
445         }
446 
447         /// <summary>
448         /// Retrieves object mapping metadata for the given type. The first time a type
449         /// is encountered, we cache the metadata to avoid repeating the work for every
450         /// row in result.
451         ///
452         /// Caching at the materializer rather than workspace/metadata cache level optimizes
453         /// for transient types (including row types produced for span, LINQ initializations,
454         /// collections and projections).
455         /// </summary>
LookupObjectMapping(EdmType edmType)456         private ObjectTypeMapping LookupObjectMapping(EdmType edmType)
457         {
458             Debug.Assert(null != edmType, "no edmType?"); // edmType must not be null.
459 
460             ObjectTypeMapping result;
461 
462             EdmType resolvedType = ResolveSpanType(edmType);
463             if (null == resolvedType)
464             {
465                 resolvedType = edmType;
466             }
467 
468             if (!_objectTypeMappings.TryGetValue(resolvedType, out result))
469             {
470                 result = Util.GetObjectMapping(resolvedType, _workspace);
471                 _objectTypeMappings.Add(resolvedType, result);
472             }
473             return result;
474         }
475 
476         /// <summary>
477         /// Remove spanned info from the edmType
478         /// </summary>
479         /// <param name="edmType"></param>
480         /// <returns></returns>
ResolveSpanType(EdmType edmType)481         private EdmType ResolveSpanType(EdmType edmType)
482         {
483             EdmType result = edmType;
484 
485             switch (result.BuiltInTypeKind)
486             {
487                 case BuiltInTypeKind.CollectionType:
488                     // For collections, we have to edmType from the (potentially) spanned
489                     // element of the collection, then build a new Collection around it.
490                     result = ResolveSpanType(((CollectionType)result).TypeUsage.EdmType);
491                     if (null != result)
492                     {
493                         result = new CollectionType(result);
494                     }
495                     break;
496 
497                 case BuiltInTypeKind.RowType:
498                     // If there is a SpanMap, pick up the EdmType from the first column
499                     // in the record, otherwise it's just the type we already have.
500                     RowType rowType = (RowType)result;
501                     if (null != _spanIndex && _spanIndex.HasSpanMap(rowType))
502                     {
503                         result = rowType.Members[0].TypeUsage.EdmType;
504                     }
505                     break;
506             }
507             return result;
508         }
509 
510         /// <summary>
511         /// Creates an expression representing an inline delegate of type Func&lt;Shaper, body.Type&gt;
512         /// </summary>
513         [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
CreateInlineDelegate(Expression body)514         private LambdaExpression CreateInlineDelegate(Expression body)
515         {
516             // Note that we call through to a typed method so that we can call Expression.Lambda<Func> instead
517             // of the straightforward Expression.Lambda. The latter requires FullTrust.
518             Type delegateReturnType = body.Type;
519             MethodInfo createMethod = Translator_TypedCreateInlineDelegate.MakeGenericMethod(delegateReturnType);
520             LambdaExpression result = (LambdaExpression)createMethod.Invoke(this, new object[] { body });
521             return result;
522         }
523 
TypedCreateInlineDelegate(Expression body)524         private Expression<Func<Shaper, T>> TypedCreateInlineDelegate<T>(Expression body)
525         {
526             Expression<Func<Shaper, T>> result = Expression.Lambda<Func<Shaper, T>>(body, Shaper_Parameter);
527             _currentCoordinatorScratchpad.AddInlineDelegate(result);
528             return result;
529         }
530 
531         #endregion
532 
533         #region Lightweight CodeGen emitters
534 
535         #region static Reflection info used in emitters
536 
537         private static readonly MethodInfo DbDataReader_GetValue = typeof(DbDataReader).GetMethod("GetValue");
538         private static readonly MethodInfo DbDataReader_GetString = typeof(DbDataReader).GetMethod("GetString");
539         private static readonly MethodInfo DbDataReader_GetInt16 = typeof(DbDataReader).GetMethod("GetInt16");
540         private static readonly MethodInfo DbDataReader_GetInt32 = typeof(DbDataReader).GetMethod("GetInt32");
541         private static readonly MethodInfo DbDataReader_GetInt64 = typeof(DbDataReader).GetMethod("GetInt64");
542         private static readonly MethodInfo DbDataReader_GetBoolean = typeof(DbDataReader).GetMethod("GetBoolean");
543         private static readonly MethodInfo DbDataReader_GetDecimal = typeof(DbDataReader).GetMethod("GetDecimal");
544         private static readonly MethodInfo DbDataReader_GetFloat = typeof(DbDataReader).GetMethod("GetFloat");
545         private static readonly MethodInfo DbDataReader_GetDouble = typeof(DbDataReader).GetMethod("GetDouble");
546         private static readonly MethodInfo DbDataReader_GetDateTime = typeof(DbDataReader).GetMethod("GetDateTime");
547         private static readonly MethodInfo DbDataReader_GetGuid = typeof(DbDataReader).GetMethod("GetGuid");
548         private static readonly MethodInfo DbDataReader_GetByte = typeof(DbDataReader).GetMethod("GetByte");
549         private static readonly MethodInfo DbDataReader_IsDBNull = typeof(DbDataReader).GetMethod("IsDBNull");
550 
551         private static readonly ConstructorInfo EntityKey_ctor_SingleKey = typeof(EntityKey).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(EntitySet), typeof(object) }, null);
552         private static readonly ConstructorInfo EntityKey_ctor_CompositeKey = typeof(EntityKey).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(EntitySet), typeof(object[]) }, null);
553 
554         private static readonly MethodInfo IEntityKeyWithKey_EntityKey = typeof(System.Data.Objects.DataClasses.IEntityWithKey).GetProperty("EntityKey").GetSetMethod();
555 
556         private static readonly MethodInfo IEqualityComparerOfString_Equals = typeof(IEqualityComparer<String>).GetMethod("Equals", new Type[] { typeof(string), typeof(string) });
557 
558         private static readonly ConstructorInfo MaterializedDataRecord_ctor = typeof(MaterializedDataRecord).GetConstructor(
559                                                                                             BindingFlags.NonPublic | BindingFlags.Instance,
560                                                                                             null, new Type[] { typeof(MetadataWorkspace), typeof(TypeUsage), typeof(object[]) },
561                                                                                             null);
562 
563         private static readonly MethodInfo RecordState_GatherData = typeof(RecordState).GetMethod("GatherData", BindingFlags.NonPublic | BindingFlags.Instance);
564         private static readonly MethodInfo RecordState_SetNullRecord = typeof(RecordState).GetMethod("SetNullRecord", BindingFlags.NonPublic | BindingFlags.Instance);
565 
566         private static readonly MethodInfo Shaper_Discriminate = typeof(Shaper).GetMethod("Discriminate");
567         private static readonly MethodInfo Shaper_GetPropertyValueWithErrorHandling = typeof(Shaper).GetMethod("GetPropertyValueWithErrorHandling");
568         private static readonly MethodInfo Shaper_GetColumnValueWithErrorHandling = typeof(Shaper).GetMethod("GetColumnValueWithErrorHandling");
569         private static readonly MethodInfo Shaper_GetGeographyColumnValue = typeof(Shaper).GetMethod("GetGeographyColumnValue");
570         private static readonly MethodInfo Shaper_GetGeometryColumnValue = typeof(Shaper).GetMethod("GetGeometryColumnValue");
571         private static readonly MethodInfo Shaper_GetSpatialColumnValueWithErrorHandling = typeof(Shaper).GetMethod("GetSpatialColumnValueWithErrorHandling");
572         private static readonly MethodInfo Shaper_GetSpatialPropertyValueWithErrorHandling = typeof(Shaper).GetMethod("GetSpatialPropertyValueWithErrorHandling");
573         private static readonly MethodInfo Shaper_HandleEntity = typeof(Shaper).GetMethod("HandleEntity");
574         private static readonly MethodInfo Shaper_HandleEntityAppendOnly = typeof(Shaper).GetMethod("HandleEntityAppendOnly");
575         private static readonly MethodInfo Shaper_HandleEntityNoTracking = typeof(Shaper).GetMethod("HandleEntityNoTracking");
576         private static readonly MethodInfo Shaper_HandleFullSpanCollection = typeof(Shaper).GetMethod("HandleFullSpanCollection");
577         private static readonly MethodInfo Shaper_HandleFullSpanElement = typeof(Shaper).GetMethod("HandleFullSpanElement");
578         private static readonly MethodInfo Shaper_HandleIEntityWithKey = typeof(Shaper).GetMethod("HandleIEntityWithKey");
579         private static readonly MethodInfo Shaper_HandleRelationshipSpan = typeof(Shaper).GetMethod("HandleRelationshipSpan");
580         private static readonly MethodInfo Shaper_SetColumnValue = typeof(Shaper).GetMethod("SetColumnValue");
581         private static readonly MethodInfo Shaper_SetEntityRecordInfo = typeof(Shaper).GetMethod("SetEntityRecordInfo");
582         private static readonly MethodInfo Shaper_SetState = typeof(Shaper).GetMethod("SetState");
583         private static readonly MethodInfo Shaper_SetStatePassthrough = typeof(Shaper).GetMethod("SetStatePassthrough");
584 
585         private static readonly MethodInfo Translator_BinaryEquals = typeof(Translator).GetMethod("BinaryEquals", BindingFlags.NonPublic | BindingFlags.Static);
586         private static readonly MethodInfo Translator_CheckedConvert = typeof(Translator).GetMethod("CheckedConvert", BindingFlags.NonPublic | BindingFlags.Static);
587         private static readonly MethodInfo Translator_Compile = typeof(Translator).GetMethod("Compile", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(Expression) }, null);
588         private static readonly MethodInfo Translator_MultipleDiscriminatorPolymorphicColumnMapHelper = typeof(Translator).GetMethod("MultipleDiscriminatorPolymorphicColumnMapHelper", BindingFlags.NonPublic | BindingFlags.Instance);
589         private static readonly MethodInfo Translator_TypedCreateInlineDelegate = typeof(Translator).GetMethod("TypedCreateInlineDelegate", BindingFlags.NonPublic | BindingFlags.Instance);
590 
591         private static readonly PropertyInfo EntityWrapperFactory_NullWrapper = typeof(EntityWrapperFactory).GetProperty("NullWrapper", BindingFlags.Static | BindingFlags.NonPublic);
592         private static readonly PropertyInfo IEntityWrapper_Entity = typeof(IEntityWrapper).GetProperty("Entity");
593         private static readonly MethodInfo EntityProxyTypeInfo_SetEntityWrapper = typeof(EntityProxyTypeInfo).GetMethod("SetEntityWrapper", BindingFlags.NonPublic | BindingFlags.Instance);
594 
595         private static readonly ConstructorInfo PocoPropertyAccessorStrategy_ctor = typeof(PocoPropertyAccessorStrategy).GetConstructor(new Type[] { typeof(object) });
596         private static readonly ConstructorInfo EntityWithChangeTrackerStrategy_ctor = typeof(EntityWithChangeTrackerStrategy).GetConstructor(new Type[] { typeof(IEntityWithChangeTracker) });
597         private static readonly ConstructorInfo EntityWithKeyStrategy_ctor = typeof(EntityWithKeyStrategy).GetConstructor(new Type[] { typeof(IEntityWithKey) });
598         private static readonly ConstructorInfo PocoEntityKeyStrategy_ctor = typeof(PocoEntityKeyStrategy).GetConstructor(new Type[0]);
599         private static readonly PropertyInfo SnapshotChangeTrackingStrategy_Instance = typeof(SnapshotChangeTrackingStrategy).GetProperty("Instance", BindingFlags.Static | BindingFlags.Public);
600 
601         private static readonly MethodInfo EntityWrapperFactory_GetPocoPropertyAccessorStrategyFunc = typeof(EntityWrapperFactory).GetMethod("GetPocoPropertyAccessorStrategyFunc", BindingFlags.NonPublic | BindingFlags.Static);
602         private static readonly MethodInfo EntityWrapperFactory_GetNullPropertyAccessorStrategyFunc = typeof(EntityWrapperFactory).GetMethod("GetNullPropertyAccessorStrategyFunc", BindingFlags.NonPublic | BindingFlags.Static);
603         private static readonly MethodInfo EntityWrapperFactory_GetEntityWithChangeTrackerStrategyFunc = typeof(EntityWrapperFactory).GetMethod("GetEntityWithChangeTrackerStrategyFunc", BindingFlags.NonPublic | BindingFlags.Static);
604         private static readonly MethodInfo EntityWrapperFactory_GetSnapshotChangeTrackingStrategyFunc = typeof(EntityWrapperFactory).GetMethod("GetSnapshotChangeTrackingStrategyFunc", BindingFlags.NonPublic | BindingFlags.Static);
605         private static readonly MethodInfo EntityWrapperFactory_GetEntityWithKeyStrategyStrategyFunc = typeof(EntityWrapperFactory).GetMethod("GetEntityWithKeyStrategyStrategyFunc", BindingFlags.NonPublic | BindingFlags.Static);
606         private static readonly MethodInfo EntityWrapperFactory_GetPocoEntityKeyStrategyFunc = typeof(EntityWrapperFactory).GetMethod("GetPocoEntityKeyStrategyFunc", BindingFlags.NonPublic | BindingFlags.Static);
607 
608         #endregion
609 
610         #region static expressions used in emitters
611 
612         private static readonly Expression DBNull_Value = Expression.Constant(DBNull.Value, typeof(object));
613 
614         internal static readonly ParameterExpression Shaper_Parameter = Expression.Parameter(typeof(Shaper), "shaper");
615         private static readonly ParameterExpression EntityParameter = Expression.Parameter(typeof(object), "entity");
616 
617         internal static readonly Expression Shaper_Reader = Expression.Field(Shaper_Parameter, typeof(Shaper).GetField("Reader"));
618         private static readonly Expression Shaper_Workspace = Expression.Field(Shaper_Parameter, typeof(Shaper).GetField("Workspace"));
619         private static readonly Expression Shaper_State = Expression.Field(Shaper_Parameter, typeof(Shaper).GetField("State"));
620         private static readonly Expression Shaper_Context = Expression.Field(Shaper_Parameter, typeof(Shaper).GetField("Context"));
621         private static readonly Expression Shaper_Context_Options = Expression.Property(Shaper_Context, typeof(ObjectContext).GetProperty("ContextOptions"));
622         private static readonly Expression Shaper_ProxyCreationEnabled = Expression.Property(Shaper_Context_Options, typeof(ObjectContextOptions).GetProperty("ProxyCreationEnabled"));
623 
624         #endregion
625 
626         /// <summary>
627         /// Create expression to AndAlso the expressions and return the result.
628         /// </summary>
Emit_AndAlso(IEnumerable<Expression> operands)629         private static Expression Emit_AndAlso(IEnumerable<Expression> operands)
630         {
631             Expression result = null;
632             foreach (Expression operand in operands)
633             {
634                 if (result == null)
635                 {
636                     result = operand;
637                 }
638                 else
639                 {
640                     result = Expression.AndAlso(result, operand);
641                 }
642             }
643             return result;
644         }
645 
646         /// <summary>
647         /// Create expression to bitwise-or the expressions and return the result.
648         /// </summary>
Emit_BitwiseOr(IEnumerable<Expression> operands)649         private static Expression Emit_BitwiseOr(IEnumerable<Expression> operands)
650         {
651             Expression result = null;
652             foreach (Expression operand in operands)
653             {
654                 if (result == null)
655                 {
656                     result = operand;
657                 }
658                 else
659                 {
660                     result = Expression.Or(result, operand);
661                 }
662             }
663             return result;
664         }
665 
666         /// <summary>
667         /// Creates an expression with null value. If the given type cannot be assigned
668         /// a null value, we create a value that throws when materializing. We don't throw statically
669         /// because we consistently defer type checks until materialization.
670         ///
671         /// See SQL BU 588980.
672         /// </summary>
673         /// <param name="type">Type of null expression.</param>
674         /// <returns>Null expression.</returns>
Emit_NullConstant(Type type)675         internal static Expression Emit_NullConstant(Type type)
676         {
677             Expression nullConstant;
678             EntityUtil.CheckArgumentNull(type, "type");
679 
680             // check if null can be assigned to the type
681             if (type.IsClass || TypeSystem.IsNullableType(type))
682             {
683                 // create the constant directly if it accepts null
684                 nullConstant = Expression.Constant(null, type);
685             }
686             else
687             {
688                 // create (object)null and then cast to the type
689                 nullConstant = Emit_EnsureType(Expression.Constant(null, typeof(object)), type);
690             }
691             return nullConstant;
692         }
693 
694         /// <summary>
695         /// Emits an expression that represnts a NullEntityWrapper instance.
696         /// </summary>
697         /// <param name="type">The type of the null to be wrapped</param>
698         /// <returns>An expression represnting a wrapped null</returns>
Emit_WrappedNullConstant(Type type)699         internal static Expression Emit_WrappedNullConstant(Type type)
700         {
701             return Expression.Property(null, EntityWrapperFactory_NullWrapper);
702         }
703 
704         /// <summary>
705         /// Create expression that guarantees the input expression is of the specified
706         /// type; no Convert is added if the expression is already of the same type.
707         ///
708         /// Internal because it is called from the TranslatorResult.
709         /// </summary>
Emit_EnsureType(Expression input, Type type)710         internal static Expression Emit_EnsureType(Expression input, Type type)
711         {
712             Expression result = input;
713             if (input.Type != type && !typeof(IEntityWrapper).IsAssignableFrom(input.Type))
714             {
715                 if (type.IsAssignableFrom(input.Type))
716                 {
717                     // simple convert, just to make sure static type checks succeed
718                     result = Expression.Convert(input, type);
719                 }
720                 else
721                 {
722                     // user is asking for the 'wrong' type... add exception handling
723                     // in case of failure
724                     MethodInfo checkedConvertMethod = Translator_CheckedConvert.MakeGenericMethod(input.Type, type);
725                     result = Expression.Call(checkedConvertMethod, input);
726                 }
727             }
728             return result;
729         }
730 
731         /// <summary>
732         /// Uses Emit_EnsureType and then wraps the result in an IEntityWrapper instance.
733         /// </summary>
734         /// <param name="input">The expression that creates the entity to be wrapped</param>
735         /// <param name="keyReader">Expression to read the entity key</param>
736         /// <param name="entitySetReader">Expression to read the entity set</param>
737         /// <param name="requestedType">The type that was actuall requested by the client--may be object</param>
738         /// <param name="identityType">The type of the identity type of the entity being materialized--never a proxy type</param>
739         /// <param name="actualType">The actual type being materialized--may be a proxy type</param>
740         /// <param name="mergeOption">Either NoTracking or AppendOnly depending on whether the entity is to be tracked</param>
741         /// <param name="isProxy">If true, then a proxy is being created</param>
742         /// <returns>An expression representing the IEntityWrapper for the new entity</returns>
Emit_EnsureTypeAndWrap(Expression input, Expression keyReader, Expression entitySetReader, Type requestedType, Type identityType, Type actualType, MergeOption mergeOption, bool isProxy)743         internal static Expression Emit_EnsureTypeAndWrap(Expression input, Expression keyReader, Expression entitySetReader, Type requestedType, Type identityType, Type actualType, MergeOption mergeOption, bool isProxy)
744         {
745             Expression result = Emit_EnsureType(input, requestedType); // Needed to ensure appropriate exception is thrown
746             if (!requestedType.IsClass)
747             {
748                 result = Emit_EnsureType(input, typeof(object));
749             }
750             result = Emit_EnsureType(result, actualType); // Needed to ensure appropriate type for wrapper constructor
751             return CreateEntityWrapper(result, keyReader, entitySetReader, actualType, identityType, mergeOption, isProxy);
752         }
753 
754         /// <summary>
755         /// Returns an expression that creates an IEntityWrapper approprioate for the type of entity being materialized.
756         /// </summary>
CreateEntityWrapper(Expression input, Expression keyReader, Expression entitySetReader, Type actualType, Type identityType, MergeOption mergeOption, bool isProxy)757         private static Expression CreateEntityWrapper(Expression input, Expression keyReader, Expression entitySetReader, Type actualType, Type identityType, MergeOption mergeOption, bool isProxy)
758         {
759             Expression result;
760             bool isIEntityWithKey = typeof(IEntityWithKey).IsAssignableFrom(actualType);
761             bool isIEntityWithRelationships = typeof(IEntityWithRelationships).IsAssignableFrom(actualType);
762             bool isIEntityWithChangeTracker = typeof(IEntityWithChangeTracker).IsAssignableFrom(actualType);
763             if (isIEntityWithRelationships && isIEntityWithChangeTracker && isIEntityWithKey && !isProxy)
764             {
765                 // This is the case where all our interfaces are implemented by the entity and we are not creating a proxy.
766                 // This is the case that absolutely must be kept fast.  It is a simple call to the wrapper constructor.
767                 Type genericType = typeof(LightweightEntityWrapper<>).MakeGenericType(actualType);
768                 ConstructorInfo ci = genericType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.CreateInstance, null,
769                                                                 new Type[] { actualType, typeof(EntityKey), typeof(EntitySet), typeof(ObjectContext), typeof(MergeOption), typeof(Type) }, null);
770                 result = Expression.New(ci, input, keyReader, entitySetReader, Shaper_Context, Expression.Constant(mergeOption, typeof(MergeOption)), Expression.Constant(identityType, typeof(Type)));
771             }
772             else
773             {
774                 // This is the general case.  We choose various strategy objects based on the interfaces implemented and
775                 // whether or not we are creating a proxy.
776                 // We pass in lambdas to create the strategy objects so that they can have the materialized entity as
777                 // a parameter while still being set in the wrapper constructor.
778                 Expression propertyAccessorStrategy = !isIEntityWithRelationships || isProxy ?
779                                                       Expression.Call(EntityWrapperFactory_GetPocoPropertyAccessorStrategyFunc) :
780                                                       Expression.Call(EntityWrapperFactory_GetNullPropertyAccessorStrategyFunc);
781 
782                 Expression keyStrategy = isIEntityWithKey ?
783                                          Expression.Call(EntityWrapperFactory_GetEntityWithKeyStrategyStrategyFunc) :
784                                          Expression.Call(EntityWrapperFactory_GetPocoEntityKeyStrategyFunc);
785 
786                 Expression changeTrackingStrategy = isIEntityWithChangeTracker ?
787                                                     Expression.Call(EntityWrapperFactory_GetEntityWithChangeTrackerStrategyFunc) :
788                                                     Expression.Call(EntityWrapperFactory_GetSnapshotChangeTrackingStrategyFunc);
789 
790                 Type genericType = isIEntityWithRelationships ?
791                                    typeof(EntityWrapperWithRelationships<>).MakeGenericType(actualType) :
792                                    typeof(EntityWrapperWithoutRelationships<>).MakeGenericType(actualType);
793 
794                 ConstructorInfo ci = genericType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.CreateInstance, null,
795                                                                 new Type[] { actualType, typeof(EntityKey), typeof(EntitySet), typeof(ObjectContext), typeof(MergeOption), typeof(Type),
796                                                                              typeof(Func<object, IPropertyAccessorStrategy>), typeof(Func<object, IChangeTrackingStrategy>), typeof(Func<object, IEntityKeyStrategy>) }, null);
797                 result = Expression.New(ci, input, keyReader, entitySetReader, Shaper_Context, Expression.Constant(mergeOption, typeof(MergeOption)), Expression.Constant(identityType, typeof(Type)),
798                                         propertyAccessorStrategy, changeTrackingStrategy, keyStrategy);
799             }
800             result = Expression.Convert(result, typeof(IEntityWrapper));
801             return result;
802         }
803 
804         /// <summary>
805         /// Takes an expression that represents an IEntityWrapper instance and creates a new
806         /// expression that extracts the raw entity from this.
807         /// </summary>
Emit_UnwrapAndEnsureType(Expression input, Type type)808         internal static Expression Emit_UnwrapAndEnsureType(Expression input, Type type)
809         {
810             return Translator.Emit_EnsureType(Expression.Property(input, IEntityWrapper_Entity), type);
811         }
812 
813         /// <summary>
814         /// Method that the generated expression calls when the types are not
815         /// assignable
816         /// </summary>
CheckedConvert(TSource value)817         private static TTarget CheckedConvert<TSource, TTarget>(TSource value)
818         {
819             checked
820             {
821                 try
822                 {
823                     return (TTarget)(object)value;
824                 }
825                 catch (InvalidCastException)
826                 {
827                     Type valueType = value.GetType();
828 
829                     // In the case of CompensatingCollection<T>, simply report IEnumerable<T> in the
830                     // exception message because the user has no reason to know what the type represents.
831                     if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(CompensatingCollection<>))
832                     {
833                         valueType = typeof(IEnumerable<>).MakeGenericType(valueType.GetGenericArguments());
834                     }
835                     throw EntityUtil.ValueInvalidCast(valueType, typeof(TTarget));
836                 }
837                 catch (NullReferenceException)
838                 {
839                     throw EntityUtil.ValueNullReferenceCast(typeof(TTarget));
840                 }
841             }
842         }
843 
844         /// <summary>
845         /// Create expression to compare the results of two expressions and return
846         /// whether they are equal.  Note we have special case logic for byte arrays.
847         /// </summary>
Emit_Equal(Expression left, Expression right)848         private static Expression Emit_Equal(Expression left, Expression right)
849         {
850             Expression result;
851             Debug.Assert(left.Type == right.Type, "equals with different types");
852             if (typeof(byte[]) == left.Type)
853             {
854                 result = Expression.Call(Translator_BinaryEquals, left, right);
855             }
856             else
857             {
858                 result = Expression.Equal(left, right);
859             }
860             return result;
861         }
862 
863         /// <summary>
864         /// Helper method used in expressions generated by Emit_Equal to perform a
865         /// byte-by-byte comparison of two byte arrays.  There really ought to be
866         /// a way to do this in the framework but I'm unaware of it.
867         /// </summary>
BinaryEquals(byte[] left, byte[] right)868         private static bool BinaryEquals(byte[] left, byte[] right)
869         {
870             if (null == left)
871             {
872                 return null == right;
873             }
874             else if (null == right)
875             {
876                 return false;
877             }
878             if (left.Length != right.Length)
879             {
880                 return false;
881             }
882             for (int i = 0; i < left.Length; i++)
883             {
884                 if (left[i] != right[i])
885                 {
886                     return false;
887                 }
888             }
889             return true;
890         }
891 
892         /// <summary>
893         /// Creates expression to construct an EntityKey. Assumes that both the key has
894         /// a value (Emit_EntityKey_HasValue == true) and that the EntitySet has value
895         /// (EntitySet != null).
896         /// </summary>
Emit_EntityKey_ctor(Translator translator, EntityIdentity entityIdentity, bool isForColumnValue, out Expression entitySetReader)897         private static Expression Emit_EntityKey_ctor(Translator translator, EntityIdentity entityIdentity, bool isForColumnValue, out Expression entitySetReader)
898         {
899             Expression result;
900             Expression setEntitySetStateSlotValue = null;
901 
902             // First build the expressions that read each value that comprises the EntityKey
903             List<Expression> keyReaders = new List<Expression>(entityIdentity.Keys.Length);
904             for (int i = 0; i < entityIdentity.Keys.Length; i++)
905             {
906                 Expression keyReader = entityIdentity.Keys[i].Accept(translator, new TranslatorArg(typeof(object))).Expression;
907                 keyReaders.Add(keyReader);
908             }
909 
910             // Next build the expression that determines us the entitySet; how we do this differs
911             // depending on whether we have a simple or discriminated identity.
912 
913             SimpleEntityIdentity simpleEntityIdentity = entityIdentity as SimpleEntityIdentity;
914             if (null != simpleEntityIdentity)
915             {
916                 if (simpleEntityIdentity.EntitySet == null)
917                 {
918                     // 'Free-floating' entities do not have entity keys.
919                     entitySetReader = Expression.Constant(null, typeof(EntitySet));
920                     return Expression.Constant(null, typeof(EntityKey));
921                 }
922                 // For SimpleEntityIdentities, the entitySet expression is a constant
923                 entitySetReader = Expression.Constant(simpleEntityIdentity.EntitySet, typeof(EntitySet));
924             }
925             else
926             {
927                 // For DiscriminatedEntityIdentities, the we have to search the EntitySetMap
928                 // for the matching discriminator value; we'll get the discriminator first,
929                 // the compare them all in sequence.
930                 DiscriminatedEntityIdentity discriminatedEntityIdentity = (DiscriminatedEntityIdentity)entityIdentity;
931 
932                 Expression discriminator = discriminatedEntityIdentity.EntitySetColumnMap.Accept(translator, new TranslatorArg(typeof(int?))).Expression;
933                 EntitySet[] entitySets = discriminatedEntityIdentity.EntitySetMap;
934 
935                 //
936 
937 
938                 // (_discriminator == 0 ? entitySets[0] : (_discriminator == 1 ? entitySets[1] ... : null)
939                 entitySetReader = Expression.Constant(null, typeof(EntitySet));
940                 for (int i = 0; i < entitySets.Length; i++)
941                 {
942                     entitySetReader = Expression.Condition(
943                                                 Expression.Equal(discriminator, Expression.Constant(i, typeof(int?))),
944                                                 Expression.Constant(entitySets[i], typeof(EntitySet)),
945                                                 entitySetReader
946                                                 );
947                 }
948 
949                 // Allocate a stateSlot to contain the entitySet we determine, and ensure we
950                 // store it there on the way to constructing the key.
951                 int entitySetStateSlotNumber = translator.AllocateStateSlot();
952                 setEntitySetStateSlotValue = Emit_Shaper_SetStatePassthrough(entitySetStateSlotNumber, entitySetReader);
953                 entitySetReader = Emit_Shaper_GetState(entitySetStateSlotNumber, typeof(EntitySet));
954             }
955 
956             // And now that we have all the pieces, construct the EntityKey using the appropriate
957             // constructor (there's an optimized constructor for the single key case)
958             if (1 == entityIdentity.Keys.Length)
959             {
960                 // new EntityKey(entitySet, keyReaders[0])
961                 result = Expression.New(EntityKey_ctor_SingleKey,
962                                             entitySetReader,
963                                             keyReaders[0]);
964             }
965             else
966             {
967                 // new EntityKey(entitySet, { keyReaders[0], ... keyReaders[n] })
968                 result = Expression.New(EntityKey_ctor_CompositeKey,
969                                             entitySetReader,
970                                             Expression.NewArrayInit(typeof(object), keyReaders));
971             }
972 
973             // In the case where we've had to store the entitySetReader value in a
974             // state slot, we test the value for non-null before we construct the
975             // entityKey.  We use this opportunity to stuff the value into the state
976             // slot, so the code above that attempts to read it from there will find
977             // it.
978             if (null != setEntitySetStateSlotValue)
979             {
980                 Expression noEntityKeyExpression;
981                 if (translator.IsValueLayer && !isForColumnValue)
982                 {
983                     noEntityKeyExpression = Expression.Constant(EntityKey.NoEntitySetKey, typeof(EntityKey));
984                 }
985                 else
986                 {
987                     noEntityKeyExpression = Expression.Constant(null, typeof(EntityKey));
988                 }
989                 result = Expression.Condition(
990                                             Expression.Equal(setEntitySetStateSlotValue, Expression.Constant(null, typeof(EntitySet))),
991                                             noEntityKeyExpression,
992                                             result
993                                             );
994             }
995             return result;
996         }
997 
998         /// <summary>
999         /// Create expression that verifies that the entityKey has a value.  Note we just
1000         /// presume that if the first key is non-null, all the keys will be valid.
1001         /// </summary>
Emit_EntityKey_HasValue(SimpleColumnMap[] keyColumns)1002         private static Expression Emit_EntityKey_HasValue(SimpleColumnMap[] keyColumns)
1003         {
1004             Debug.Assert(0 < keyColumns.Length, "empty keyColumns?");
1005 
1006             // !shaper.Reader.IsDBNull(keyColumn[0].ordinal)
1007             Expression result = Emit_Reader_IsDBNull(keyColumns[0]);
1008             result = Expression.Not(result);
1009             return result;
1010         }
1011 
1012         /// <summary>
1013         /// Create expression to call the GetValue method of the shaper's source data reader
1014         /// </summary>
Emit_Reader_GetValue(int ordinal, Type type)1015         private static Expression Emit_Reader_GetValue(int ordinal, Type type)
1016         {
1017             // (type)shaper.Reader.GetValue(ordinal)
1018             Expression result = Emit_EnsureType(Expression.Call(Shaper_Reader, DbDataReader_GetValue, Expression.Constant(ordinal)), type);
1019             return result;
1020         }
1021 
1022         /// <summary>
1023         /// Create expression to call the IsDBNull method of the shaper's source data reader
1024         /// </summary>
Emit_Reader_IsDBNull(int ordinal)1025         private static Expression Emit_Reader_IsDBNull(int ordinal)
1026         {
1027             // shaper.Reader.IsDBNull(ordinal)
1028             Expression result = Expression.Call(Shaper_Reader, DbDataReader_IsDBNull, Expression.Constant(ordinal));
1029             return result;
1030         }
1031 
1032         /// <summary>
1033         /// Create expression to call the IsDBNull method of the shaper's source data reader
1034         /// for the scalar column represented by the column map.
1035         /// </summary>
Emit_Reader_IsDBNull(ColumnMap columnMap)1036         private static Expression Emit_Reader_IsDBNull(ColumnMap columnMap)
1037         {
1038             //
1039             Expression result = Emit_Reader_IsDBNull(((ScalarColumnMap)columnMap).ColumnPos);
1040             return result;
1041         }
1042 
1043         /// <summary>
1044         /// Create expression to read a property value with error handling
1045         /// </summary>
Emit_Shaper_GetPropertyValueWithErrorHandling(Type propertyType, int ordinal, string propertyName, string typeName, TypeUsage columnType)1046         private static Expression Emit_Shaper_GetPropertyValueWithErrorHandling(Type propertyType, int ordinal, string propertyName, string typeName, TypeUsage columnType)
1047         {
1048             // // shaper.GetSpatialColumnValueWithErrorHandling(ordinal, propertyName, typeName, primitiveColumnType) OR shaper.GetColumnValueWithErrorHandling(ordinal, propertyName, typeName)
1049             Expression result;
1050             PrimitiveTypeKind primitiveColumnType;
1051             if (Helper.IsSpatialType(columnType, out primitiveColumnType))
1052             {
1053                 result = Expression.Call(Shaper_Parameter, Shaper_GetSpatialPropertyValueWithErrorHandling.MakeGenericMethod(propertyType),
1054                     Expression.Constant(ordinal), Expression.Constant(propertyName), Expression.Constant(typeName), Expression.Constant(primitiveColumnType, typeof(PrimitiveTypeKind)));
1055             }
1056             else
1057             {
1058                 result = Expression.Call(Shaper_Parameter, Shaper_GetPropertyValueWithErrorHandling.MakeGenericMethod(propertyType), Expression.Constant(ordinal), Expression.Constant(propertyName), Expression.Constant(typeName));
1059             }
1060             return result;
1061         }
1062 
1063         /// <summary>
1064         /// Create expression to read a column value with error handling
1065         /// </summary>
Emit_Shaper_GetColumnValueWithErrorHandling(Type resultType, int ordinal, TypeUsage columnType)1066         private static Expression Emit_Shaper_GetColumnValueWithErrorHandling(Type resultType, int ordinal, TypeUsage columnType)
1067         {
1068             // shaper.GetSpatialColumnValueWithErrorHandling(ordinal, primitiveColumnType) OR shaper.GetColumnValueWithErrorHandling(ordinal)
1069             Expression result;
1070             PrimitiveTypeKind primitiveColumnType;
1071             if (Helper.IsSpatialType(columnType, out primitiveColumnType))
1072             {
1073                 primitiveColumnType = Helper.IsGeographicType((PrimitiveType)columnType.EdmType) ? PrimitiveTypeKind.Geography : PrimitiveTypeKind.Geometry;
1074                 result = Expression.Call(Shaper_Parameter, Shaper_GetSpatialColumnValueWithErrorHandling.MakeGenericMethod(resultType), Expression.Constant(ordinal), Expression.Constant(primitiveColumnType, typeof(PrimitiveTypeKind)));
1075             }
1076             else
1077             {
1078                 result = Expression.Call(Shaper_Parameter, Shaper_GetColumnValueWithErrorHandling.MakeGenericMethod(resultType), Expression.Constant(ordinal));
1079             }
1080             return result;
1081         }
1082 
1083         /// <summary>
1084         /// Create expression to read a column value of type System.Data.Spatial.DbGeography by delegating to the DbSpatialServices implementation of the underlying provider
1085         /// </summary>
Emit_Shaper_GetGeographyColumnValue(int ordinal)1086         private static Expression Emit_Shaper_GetGeographyColumnValue(int ordinal)
1087         {
1088             // shaper.GetGeographyColumnValue(ordinal)
1089             Expression result = Expression.Call(Shaper_Parameter, Shaper_GetGeographyColumnValue, Expression.Constant(ordinal));
1090             return result;
1091         }
1092 
1093         /// <summary>
1094         /// Create expression to read a column value of type System.Data.Spatial.DbGeometry by delegating to the DbSpatialServices implementation of the underlying provider
1095         /// </summary>
Emit_Shaper_GetGeometryColumnValue(int ordinal)1096         private static Expression Emit_Shaper_GetGeometryColumnValue(int ordinal)
1097         {
1098             // shaper.GetGeometryColumnValue(ordinal)
1099             Expression result = Expression.Call(Shaper_Parameter, Shaper_GetGeometryColumnValue, Expression.Constant(ordinal));
1100             return result;
1101         }
1102 
1103         /// <summary>
1104         /// Create expression to read an item from the shaper's state array
1105         /// </summary>
Emit_Shaper_GetState(int stateSlotNumber, Type type)1106         private static Expression Emit_Shaper_GetState(int stateSlotNumber, Type type)
1107         {
1108             // (type)shaper.State[stateSlotNumber]
1109             Expression result = Emit_EnsureType(Expression.ArrayIndex(Shaper_State, Expression.Constant(stateSlotNumber)), type);
1110             return result;
1111         }
1112 
1113         /// <summary>
1114         /// Create expression to set an item in the shaper's state array
1115         /// </summary>
Emit_Shaper_SetState(int stateSlotNumber, Expression value)1116         private static Expression Emit_Shaper_SetState(int stateSlotNumber, Expression value)
1117         {
1118             // shaper.SetState<T>(stateSlotNumber, value)
1119             Expression result = Expression.Call(Shaper_Parameter, Shaper_SetState.MakeGenericMethod(value.Type), Expression.Constant(stateSlotNumber), value);
1120             return result;
1121         }
1122 
1123         /// <summary>
1124         /// Create expression to set an item in the shaper's state array
1125         /// </summary>
Emit_Shaper_SetStatePassthrough(int stateSlotNumber, Expression value)1126         private static Expression Emit_Shaper_SetStatePassthrough(int stateSlotNumber, Expression value)
1127         {
1128             // shaper.SetState<T>(stateSlotNumber, value)
1129             Expression result = Expression.Call(Shaper_Parameter, Shaper_SetStatePassthrough.MakeGenericMethod(value.Type), Expression.Constant(stateSlotNumber), value);
1130             return result;
1131         }
1132         #endregion
1133 
1134         #region ColumnMapVisitor implementation
1135 
1136         // utility accept that looks up CLR type
AcceptWithMappedType(Translator translator, ColumnMap columnMap, ColumnMap parent)1137         private static TranslatorResult AcceptWithMappedType(Translator translator, ColumnMap columnMap, ColumnMap parent)
1138         {
1139             Type type = translator.DetermineClrType(columnMap.Type);
1140             TranslatorResult result = columnMap.Accept(translator, new TranslatorArg(type));
1141             return result;
1142         }
1143 
1144         #region structured columns
1145 
1146         /// <summary>
1147         /// Visit(ComplexTypeColumnMap)
1148         /// </summary>
Visit(ComplexTypeColumnMap columnMap, TranslatorArg arg)1149         internal override TranslatorResult Visit(ComplexTypeColumnMap columnMap, TranslatorArg arg)
1150         {
1151             Expression result = null;
1152             Expression nullSentinelCheck = null;
1153 
1154             if (null != columnMap.NullSentinel)
1155             {
1156                 nullSentinelCheck = Emit_Reader_IsDBNull(columnMap.NullSentinel);
1157             }
1158 
1159             if (IsValueLayer)
1160             {
1161                 result = BuildExpressionToGetRecordState(columnMap, null, null, nullSentinelCheck);
1162             }
1163             else
1164             {
1165                 ComplexType complexType = (ComplexType)columnMap.Type.EdmType;
1166                 Type clrType = DetermineClrType(complexType);
1167                 ConstructorInfo constructor = GetConstructor(clrType);
1168 
1169                 // Build expressions to read the property values from the source data
1170                 // reader and bind them to their target properties
1171                 List<MemberBinding> propertyBindings = CreatePropertyBindings(columnMap, clrType, complexType.Properties);
1172 
1173                 // We have all the property bindings now; go ahead and build the expression to
1174                 // construct the type and store the property values.
1175                 result = Expression.MemberInit(Expression.New(constructor), propertyBindings);
1176 
1177                 // If there's a null sentinel, then everything above is gated upon whether
1178                 // it's value is DBNull.Value.
1179                 if (null != nullSentinelCheck)
1180                 {
1181                     // shaper.Reader.IsDBNull(nullsentinelOridinal) ? (type)null : result
1182                     result = Expression.Condition(nullSentinelCheck, Emit_NullConstant(result.Type), result);
1183                 }
1184             }
1185             return new TranslatorResult(result, arg.RequestedType);
1186         }
1187 
1188         /// <summary>
1189         /// Visit(EntityColumnMap)
1190         /// </summary>
Visit(EntityColumnMap columnMap, TranslatorArg arg)1191         internal override TranslatorResult Visit(EntityColumnMap columnMap, TranslatorArg arg)
1192         {
1193             Expression result;
1194 
1195             // Build expressions to read the entityKey and determine the entitySet. Note
1196             // that we attempt to optimize things such that we won't construct anything
1197             // that isn't needed, depending upon the interfaces the clrType derives from
1198             // and the MergeOption that was requested.
1199             //
1200             // We always need the entitySet, except when MergeOption.NoTracking
1201             //
1202             // We always need the entityKey, except when MergeOption.NoTracking and the
1203             // clrType doesn't derive from IEntityWithKey
1204             EntityIdentity entityIdentity = columnMap.EntityIdentity;
1205             Expression entitySetReader = null;
1206             Expression entityKeyReader = Emit_EntityKey_ctor(this, entityIdentity, false, out entitySetReader);
1207 
1208             if (IsValueLayer)
1209             {
1210                 Expression nullCheckExpression = Expression.Not(Emit_EntityKey_HasValue(entityIdentity.Keys));
1211                 //Expression nullCheckExpression = Emit_EntityKey_HasValue(entityIdentity.Keys);
1212                 result = BuildExpressionToGetRecordState(columnMap, entityKeyReader, entitySetReader, nullCheckExpression);
1213             }
1214             else
1215             {
1216                 Expression constructEntity = null;
1217 
1218                 EntityType cSpaceType = (EntityType)columnMap.Type.EdmType;
1219                 Debug.Assert(cSpaceType.BuiltInTypeKind == BuiltInTypeKind.EntityType, "Type was " + cSpaceType.BuiltInTypeKind);
1220                 ClrEntityType oSpaceType = (ClrEntityType)LookupObjectMapping(cSpaceType).ClrType;
1221                 Type clrType = oSpaceType.ClrType;
1222 
1223                 // Build expressions to read the property values from the source data
1224                 // reader and bind them to their target properties
1225                 List<MemberBinding> propertyBindings = CreatePropertyBindings(columnMap, clrType, cSpaceType.Properties);
1226 
1227                 // We have all the property bindings now; go ahead and build the expression to
1228                 // construct the entity or proxy and store the property values.  We'll wrap it with more
1229                 // stuff that needs to happen (or not) below.
1230                 EntityProxyTypeInfo proxyTypeInfo = EntityProxyFactory.GetProxyType(oSpaceType);
1231 
1232                 // If no proxy type exists for the entity, construct the regular entity object.
1233                 // If a proxy type does exist, examine the ObjectContext.ContextOptions.ProxyCreationEnabled flag
1234                 // to determine whether to create a regular or proxy entity object.
1235 
1236                 Expression constructNonProxyEntity = Emit_ConstructEntity(oSpaceType, propertyBindings, entityKeyReader, entitySetReader, arg, null);
1237                 if (proxyTypeInfo == null)
1238                 {
1239                     constructEntity = constructNonProxyEntity;
1240                 }
1241                 else
1242                 {
1243                     Expression constructProxyEntity = Emit_ConstructEntity(oSpaceType, propertyBindings, entityKeyReader, entitySetReader, arg, proxyTypeInfo);
1244 
1245                     constructEntity = Expression.Condition(Shaper_ProxyCreationEnabled,
1246                                                            constructProxyEntity,
1247                                                            constructNonProxyEntity);
1248                 }
1249 
1250                 // If we're tracking, call HandleEntity (or HandleIEntityWithKey or
1251                 // HandleEntityAppendOnly) as appropriate
1252                 if (MergeOption.NoTracking != _mergeOption)
1253                 {
1254                     Type actualType = proxyTypeInfo == null ? clrType : proxyTypeInfo.ProxyType;
1255                     if (typeof(IEntityWithKey).IsAssignableFrom(actualType) && MergeOption.AppendOnly != _mergeOption)
1256                     {
1257                         constructEntity = Expression.Call(Shaper_Parameter, Shaper_HandleIEntityWithKey.MakeGenericMethod(clrType),
1258                                                                                             constructEntity,
1259                                                                                             entitySetReader
1260                                                                                             );
1261                     }
1262                     else
1263                     {
1264                         if (MergeOption.AppendOnly == _mergeOption)
1265                         {
1266                             // pass through a delegate creating the entity rather than the actual entity, so we can avoid
1267                             // the cost of materialization when the entity is already in the state manager
1268 
1269                             //Func<Shaper, TEntity> entityDelegate = shaper => constructEntity(shaper);
1270                             LambdaExpression entityDelegate = CreateInlineDelegate(constructEntity);
1271                             constructEntity = Expression.Call(Shaper_Parameter, Shaper_HandleEntityAppendOnly.MakeGenericMethod(clrType),
1272                                                                                             entityDelegate,
1273                                                                                             entityKeyReader,
1274                                                                                             entitySetReader
1275                                                                                             );
1276                         }
1277                         else
1278                         {
1279                             constructEntity = Expression.Call(Shaper_Parameter, Shaper_HandleEntity.MakeGenericMethod(clrType),
1280                                                                                             constructEntity,
1281                                                                                             entityKeyReader,
1282                                                                                             entitySetReader
1283                                                                                             );
1284                         }
1285                     }
1286                 }
1287                 else
1288                 {
1289                     constructEntity = Expression.Call(Shaper_Parameter, Shaper_HandleEntityNoTracking.MakeGenericMethod(clrType),
1290                                                                                             constructEntity
1291                                                                                             );
1292                 }
1293 
1294                 // All the above is gated upon whether there really is an entity value;
1295                 // we won't bother executing anything unless there is an entityKey value,
1296                 // otherwise we'll just return a typed null.
1297                 result = Expression.Condition(
1298                                             Emit_EntityKey_HasValue(entityIdentity.Keys),
1299                                             constructEntity,
1300                                             Emit_WrappedNullConstant(arg.RequestedType)
1301                                             );
1302             }
1303 
1304             return new TranslatorResult(result, arg.RequestedType);
1305         }
1306 
Emit_ConstructEntity(EntityType oSpaceType, IEnumerable<MemberBinding> propertyBindings, Expression entityKeyReader, Expression entitySetReader, TranslatorArg arg, EntityProxyTypeInfo proxyTypeInfo)1307         private Expression Emit_ConstructEntity(EntityType oSpaceType, IEnumerable<MemberBinding> propertyBindings, Expression entityKeyReader, Expression entitySetReader, TranslatorArg arg, EntityProxyTypeInfo proxyTypeInfo)
1308         {
1309             bool isProxy = proxyTypeInfo != null;
1310             Type clrType = oSpaceType.ClrType;
1311             Type actualType;
1312 
1313             Expression constructEntity;
1314 
1315             if (isProxy)
1316             {
1317                 constructEntity = Expression.MemberInit(Expression.New(proxyTypeInfo.ProxyType), propertyBindings);
1318                 actualType = proxyTypeInfo.ProxyType;
1319             }
1320             else
1321             {
1322                 ConstructorInfo constructor = GetConstructor(clrType);
1323                 constructEntity = Expression.MemberInit(Expression.New(constructor), propertyBindings);
1324                 actualType = clrType;
1325             }
1326 
1327             // After calling the constructor, immediately create an IEntityWrapper instance for the entity.
1328             constructEntity = Emit_EnsureTypeAndWrap(constructEntity, entityKeyReader, entitySetReader, arg.RequestedType, clrType, actualType,
1329                                                      _mergeOption == MergeOption.NoTracking ? MergeOption.NoTracking : MergeOption.AppendOnly, isProxy);
1330 
1331             if (isProxy)
1332             {
1333                 // Since we created a proxy, we now need to give it a reference to the wrapper that we just created.
1334                 constructEntity = Expression.Call(Expression.Constant(proxyTypeInfo), EntityProxyTypeInfo_SetEntityWrapper, constructEntity);
1335 
1336                 if (proxyTypeInfo.InitializeEntityCollections != null)
1337                 {
1338                     constructEntity = Expression.Call(proxyTypeInfo.InitializeEntityCollections, constructEntity);
1339                 }
1340             }
1341 
1342             return constructEntity;
1343         }
1344 
1345         /// <summary>
1346         /// Prepare a list of PropertyBindings for each item in the specified property
1347         /// collection such that the mapped property of the specified clrType has its
1348         /// value set from the source data reader.
1349         ///
1350         /// Along the way we'll keep track of non-public properties and properties that
1351         /// have link demands, so we can ensure enforce them.
1352         /// </summary>
CreatePropertyBindings(StructuredColumnMap columnMap, Type clrType, ReadOnlyMetadataCollection<EdmProperty> properties)1353         private List<MemberBinding> CreatePropertyBindings(StructuredColumnMap columnMap, Type clrType, ReadOnlyMetadataCollection<EdmProperty> properties)
1354         {
1355             List<MemberBinding> result = new List<MemberBinding>(columnMap.Properties.Length);
1356 
1357             ObjectTypeMapping mapping = LookupObjectMapping(columnMap.Type.EdmType);
1358 
1359             for (int i = 0; i < columnMap.Properties.Length; i++)
1360             {
1361                 EdmProperty edmProperty = mapping.GetPropertyMap(properties[i].Name).ClrProperty;
1362 
1363                 // get MethodInfo for setter
1364                 MethodInfo propertyAccessor;
1365                 Type propertyType;
1366                 LightweightCodeGenerator.ValidateSetterProperty(edmProperty.EntityDeclaringType, edmProperty.PropertySetterHandle, out propertyAccessor, out propertyType);
1367 
1368                 // determine if any security checks are required
1369                 if (!LightweightCodeGenerator.IsPublic(propertyAccessor))
1370                 {
1371                     _hasNonPublicMembers = true;
1372                 }
1373 
1374                 // get translation of property value
1375                 Expression valueReader = columnMap.Properties[i].Accept(this, new TranslatorArg(propertyType)).Expression;
1376 
1377                 ScalarColumnMap scalarColumnMap = columnMap.Properties[i] as ScalarColumnMap;
1378                 if (null != scalarColumnMap)
1379                 {
1380                     string propertyName = propertyAccessor.Name.Substring(4); // substring to strip "set_"
1381 
1382                     // create a value reader with error handling
1383                     Expression valueReaderWithErrorHandling = Emit_Shaper_GetPropertyValueWithErrorHandling(propertyType, scalarColumnMap.ColumnPos, propertyName, propertyAccessor.DeclaringType.Name, scalarColumnMap.Type);
1384                     _currentCoordinatorScratchpad.AddExpressionWithErrorHandling(valueReader, valueReaderWithErrorHandling);
1385                 }
1386 
1387                 Type entityDeclaringType = Type.GetTypeFromHandle(edmProperty.EntityDeclaringType);
1388                 MemberBinding binding = Expression.Bind(GetProperty(propertyAccessor, entityDeclaringType), valueReader);
1389                 result.Add(binding);
1390             }
1391             return result;
1392         }
1393 
1394         /// <summary>
1395         /// Gets the PropertyInfo representing the property with which the given setter method is associated.
1396         /// This code is taken from Expression.Bind(MethodInfo) but adapted to take a type such that it
1397         /// will work in cases in which the property was declared on a generic base class.  In such cases,
1398         /// the declaringType needs to be the actual entity type, rather than the base class type.  Note that
1399         /// declaringType can be null, in which case the setterMethod.DeclaringType is used.
1400         /// </summary>
GetProperty(MethodInfo setterMethod, Type declaringType)1401         private static PropertyInfo GetProperty(MethodInfo setterMethod, Type declaringType)
1402         {
1403             if (declaringType == null)
1404             {
1405                 declaringType = setterMethod.DeclaringType;
1406             }
1407             BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
1408             foreach (PropertyInfo propertyInfo in declaringType.GetProperties(bindingAttr))
1409             {
1410                 if (propertyInfo.GetSetMethod(nonPublic: true) == setterMethod)
1411                 {
1412                     return propertyInfo;
1413                 }
1414             }
1415             Debug.Fail("Should always find a property for the setterMethod since we got the setter method from a property in the first place.");
1416             return null;
1417         }
1418 
1419         /// <summary>
1420         /// Visit(SimplePolymorphicColumnMap)
1421         /// </summary>
Visit(SimplePolymorphicColumnMap columnMap, TranslatorArg arg)1422         internal override TranslatorResult Visit(SimplePolymorphicColumnMap columnMap, TranslatorArg arg)
1423         {
1424             Expression result;
1425 
1426             // We're building a conditional ladder, where we'll compare each
1427             // discriminator value with the one from the source data reader, and
1428             // we'll pick that type if they match.
1429             Expression discriminatorReader = AcceptWithMappedType(this, columnMap.TypeDiscriminator, columnMap).Expression;
1430 
1431             if (IsValueLayer)
1432             {
1433                 result = Emit_EnsureType(
1434                                 BuildExpressionToGetRecordState(columnMap, null, null, Expression.Constant(true)),
1435                                 arg.RequestedType);
1436             }
1437             else
1438             {
1439                 result = Emit_WrappedNullConstant(arg.RequestedType); // the default
1440             }
1441 
1442             foreach (var typeChoice in columnMap.TypeChoices)
1443             {
1444                 // determine CLR type for the type choice, and don't bother adding
1445                 // this choice if it can't produce a result
1446                 Type type = DetermineClrType(typeChoice.Value.Type);
1447 
1448                 if (type.IsAbstract)
1449                 {
1450                     continue;
1451                 }
1452 
1453                 Expression discriminatorConstant = Expression.Constant(typeChoice.Key, discriminatorReader.Type);
1454                 Expression discriminatorMatches;
1455 
1456                 // For string types, we have to use a specific comparison that handles
1457                 // trailing spaces properly, not just the general equality test we use
1458                 // elsewhere.
1459                 if (discriminatorReader.Type == typeof(string))
1460                 {
1461                     discriminatorMatches = Expression.Call(Expression.Constant(TrailingSpaceStringComparer.Instance), IEqualityComparerOfString_Equals, discriminatorConstant, discriminatorReader);
1462                 }
1463                 else
1464                 {
1465                     discriminatorMatches = Emit_Equal(discriminatorConstant, discriminatorReader);
1466                 }
1467 
1468                 result = Expression.Condition(discriminatorMatches,
1469                                             typeChoice.Value.Accept(this, arg).Expression,
1470                                             result);
1471 
1472             }
1473             return new TranslatorResult(result, arg.RequestedType);
1474         }
1475 
1476         /// <summary>
1477         /// Visit(MultipleDiscriminatorPolymorphicColumnMap)
1478         /// </summary>
1479         [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
Visit(MultipleDiscriminatorPolymorphicColumnMap columnMap, TranslatorArg arg)1480         internal override TranslatorResult Visit(MultipleDiscriminatorPolymorphicColumnMap columnMap, TranslatorArg arg)
1481         {
1482             MethodInfo multipleDiscriminatorPolymorphicColumnMapHelper = Translator_MultipleDiscriminatorPolymorphicColumnMapHelper.MakeGenericMethod(arg.RequestedType);
1483             Expression result = (Expression)multipleDiscriminatorPolymorphicColumnMapHelper.Invoke(this, new object[] { columnMap, arg });
1484             return new TranslatorResult(result, arg.RequestedType);
1485         }
1486 
1487         /// <summary>
1488         /// Helper method to simplify the construction of the types; I'm just too lazy to
1489         /// create all the nested generic types needed to this by hand.
1490         /// </summary>
MultipleDiscriminatorPolymorphicColumnMapHelper(MultipleDiscriminatorPolymorphicColumnMap columnMap, TranslatorArg arg)1491         private Expression MultipleDiscriminatorPolymorphicColumnMapHelper<TElement>(MultipleDiscriminatorPolymorphicColumnMap columnMap, TranslatorArg arg)
1492         {
1493             // construct an array of discriminator values
1494             Expression[] discriminatorReaders = new Expression[columnMap.TypeDiscriminators.Length];
1495             for (int i = 0; i < discriminatorReaders.Length; i++)
1496             {
1497                 discriminatorReaders[i] = columnMap.TypeDiscriminators[i].Accept(this, new TranslatorArg(typeof(object))).Expression;
1498             }
1499             Expression discriminatorValues = Expression.NewArrayInit(typeof(object), discriminatorReaders);
1500 
1501             // Next build the expressions that will construct the type choices. An array of KeyValuePair<EntityType, Func<Shaper, TElement>>
1502             List<Expression> elementDelegates = new List<Expression>();
1503             Type typeDelegatePairType = typeof(KeyValuePair<EntityType, Func<Shaper, TElement>>);
1504             ConstructorInfo typeDelegatePairConstructor = typeDelegatePairType.GetConstructor(new Type[] { typeof(EntityType), typeof(Func<Shaper, TElement>) });
1505             foreach (var typeChoice in columnMap.TypeChoices)
1506             {
1507                 Expression typeReader = Emit_EnsureType(AcceptWithMappedType(this, typeChoice.Value, columnMap).UnwrappedExpression, typeof(TElement));
1508                 LambdaExpression typeReaderDelegate = CreateInlineDelegate(typeReader);
1509                 Expression typeDelegatePair = Expression.New(
1510                                                     typeDelegatePairConstructor,
1511                                                     Expression.Constant(typeChoice.Key),
1512                                                     typeReaderDelegate
1513                                                     );
1514                 elementDelegates.Add(typeDelegatePair);
1515             }
1516 
1517             // invoke shaper.Discrimate({ discriminatorValue1...discriminatorValueN }, discriminateDelegate, elementDelegates)
1518             MethodInfo shaperDiscriminateOfT = Shaper_Discriminate.MakeGenericMethod(typeof(TElement));
1519             Expression result = Expression.Call(Shaper_Parameter, shaperDiscriminateOfT,
1520                                                     discriminatorValues,
1521                                                     Expression.Constant(columnMap.Discriminate),
1522                                                     Expression.NewArrayInit(typeDelegatePairType, elementDelegates)
1523                                                     );
1524             return result;
1525         }
1526 
1527         /// <summary>
1528         /// Visit(RecordColumnMap)
1529         /// </summary>
Visit(RecordColumnMap columnMap, TranslatorArg arg)1530         internal override TranslatorResult Visit(RecordColumnMap columnMap, TranslatorArg arg)
1531         {
1532             Expression result = null;
1533             Expression nullSentinelCheck = null;
1534 
1535             if (null != columnMap.NullSentinel)
1536             {
1537                 nullSentinelCheck = Emit_Reader_IsDBNull(columnMap.NullSentinel);
1538             }
1539 
1540             if (IsValueLayer)
1541             {
1542                 result = BuildExpressionToGetRecordState(columnMap, null, null, nullSentinelCheck);
1543             }
1544             else
1545             {
1546                 Debug.Assert(columnMap.Type.EdmType.BuiltInTypeKind == BuiltInTypeKind.RowType, "RecordColumnMap without RowType?"); // we kind of depend upon this
1547                 Expression nullConstant;
1548 
1549                 // There are (at least) three different reasons we have a RecordColumnMap
1550                 // so pick the method that handles the reason we have for this one.
1551                 InitializerMetadata initializerMetadata;
1552                 if (InitializerMetadata.TryGetInitializerMetadata(columnMap.Type, out initializerMetadata))
1553                 {
1554                     result = HandleLinqRecord(columnMap, initializerMetadata);
1555                     nullConstant = Emit_NullConstant(result.Type);
1556                 }
1557                 else
1558                 {
1559                     RowType spanRowType = (RowType)columnMap.Type.EdmType;
1560 
1561                     if (null != _spanIndex && _spanIndex.HasSpanMap(spanRowType))
1562                     {
1563                         result = HandleSpandexRecord(columnMap, arg, spanRowType);
1564                         nullConstant = Emit_WrappedNullConstant(result.Type);
1565                     }
1566                     else
1567                     {
1568                         result = HandleRegularRecord(columnMap, arg, spanRowType);
1569                         nullConstant = Emit_NullConstant(result.Type);
1570                     }
1571                 }
1572 
1573                 // If there is a null sentinel process it accordingly.
1574                 if (null != nullSentinelCheck)
1575                 {
1576                     // shaper.Reader.IsDBNull(nullsentinelOridinal) ? (type)null : result
1577                     result = Expression.Condition(nullSentinelCheck, nullConstant, result);
1578                 }
1579             }
1580             return new TranslatorResult(result, arg.RequestedType);
1581         }
1582 
BuildExpressionToGetRecordState(StructuredColumnMap columnMap, Expression entityKeyReader, Expression entitySetReader, Expression nullCheckExpression)1583         private Expression BuildExpressionToGetRecordState(StructuredColumnMap columnMap, Expression entityKeyReader, Expression entitySetReader, Expression nullCheckExpression)
1584         {
1585             RecordStateScratchpad recordStateScratchpad = _currentCoordinatorScratchpad.CreateRecordStateScratchpad();
1586 
1587             int stateSlotNumber = AllocateStateSlot();
1588             recordStateScratchpad.StateSlotNumber = stateSlotNumber;
1589 
1590             int propertyCount = columnMap.Properties.Length;
1591             int readerCount = (null != entityKeyReader) ? propertyCount + 1 : propertyCount;
1592 
1593             recordStateScratchpad.ColumnCount = propertyCount;
1594 
1595             // We can have an entity here, even though it's a RecordResultColumn, because
1596             // it may be a polymorphic type; eg: TREAT(Product AS DiscontinuedProduct); we
1597             // construct an EntityRecordInfo with a sentinel EntityNotValidKey as it's Key
1598             EntityType entityTypeMetadata = null;
1599             if (TypeHelpers.TryGetEdmType<EntityType>(columnMap.Type, out entityTypeMetadata))
1600             {
1601                 recordStateScratchpad.DataRecordInfo = new EntityRecordInfo(entityTypeMetadata, EntityKey.EntityNotValidKey, null);
1602             }
1603             else
1604             {
1605                 TypeUsage edmType = Helper.GetModelTypeUsage(columnMap.Type);
1606                 recordStateScratchpad.DataRecordInfo = new DataRecordInfo(edmType);
1607             }
1608 
1609             Expression[] propertyReaders = new Expression[readerCount];
1610             string[] propertyNames = new string[recordStateScratchpad.ColumnCount];
1611             TypeUsage[] typeUsages = new TypeUsage[recordStateScratchpad.ColumnCount];
1612 
1613             for (int ordinal = 0; ordinal < propertyCount; ordinal++)
1614             {
1615                 Expression propertyReader = columnMap.Properties[ordinal].Accept(this, new TranslatorArg(typeof(Object))).Expression;
1616 
1617                 // recordState.SetColumnValue(i, propertyReader ?? DBNull.Value)
1618                 propertyReaders[ordinal] = Expression.Call(Shaper_Parameter, Shaper_SetColumnValue,
1619                                                         Expression.Constant(stateSlotNumber),
1620                                                         Expression.Constant(ordinal),
1621                                                         Expression.Coalesce(propertyReader, DBNull_Value)
1622                                                     );
1623 
1624                 propertyNames[ordinal] = columnMap.Properties[ordinal].Name;
1625                 typeUsages[ordinal] = columnMap.Properties[ordinal].Type;
1626             }
1627 
1628             if (null != entityKeyReader)
1629             {
1630                 propertyReaders[readerCount - 1] = Expression.Call(Shaper_Parameter, Shaper_SetEntityRecordInfo,
1631                                                         Expression.Constant(stateSlotNumber),
1632                                                         entityKeyReader,
1633                                                         entitySetReader);
1634             }
1635 
1636             recordStateScratchpad.GatherData = Emit_BitwiseOr(propertyReaders);
1637             recordStateScratchpad.PropertyNames = propertyNames;
1638             recordStateScratchpad.TypeUsages = typeUsages;
1639 
1640             // Finally, build the expression to read the recordState from the shaper state
1641 
1642             // (RecordState)shaperState.State[stateSlotNumber].GatherData(shaper)
1643             Expression result = Expression.Call(Emit_Shaper_GetState(stateSlotNumber, typeof(RecordState)), RecordState_GatherData, Shaper_Parameter);
1644 
1645             // If there's a null check, then everything above is gated upon whether
1646             // it's value is DBNull.Value.
1647             if (null != nullCheckExpression)
1648             {
1649                 Expression nullResult = Expression.Call(Emit_Shaper_GetState(stateSlotNumber, typeof(RecordState)), RecordState_SetNullRecord, Shaper_Parameter);
1650                 // nullCheckExpression ? (type)null : result
1651                 result = Expression.Condition(nullCheckExpression, nullResult, result);
1652             }
1653             return result;
1654         }
1655 
1656         /// <summary>
1657         /// Build expression to materialize LINQ initialization types (anonymous
1658         /// types, IGrouping, EntityCollection)
1659         /// </summary>
HandleLinqRecord(RecordColumnMap columnMap, InitializerMetadata initializerMetadata)1660         private Expression HandleLinqRecord(RecordColumnMap columnMap, InitializerMetadata initializerMetadata)
1661         {
1662             List<TranslatorResult> propertyReaders = new List<TranslatorResult>(columnMap.Properties.Length);
1663 
1664             foreach (var pair in columnMap.Properties.Zip(initializerMetadata.GetChildTypes()))
1665             {
1666                 ColumnMap propertyColumnMap = pair.Key;
1667                 Type type = pair.Value;
1668 
1669                 // Note that we're not just blindly using the type from the column map
1670                 // because we need to match the type thatthe initializer says it needs;
1671                 // that's why were not using AcceptWithMappedType;
1672                 if (null == type)
1673                 {
1674                     type = DetermineClrType(propertyColumnMap.Type);
1675                 }
1676 
1677                 TranslatorResult propertyReader = propertyColumnMap.Accept(this, new TranslatorArg(type));
1678                 propertyReaders.Add(propertyReader);
1679             }
1680 
1681             Expression result = initializerMetadata.Emit(this, propertyReaders);
1682             return result;
1683         }
1684 
1685         /// <summary>
1686         /// Build expression to materialize a data record.
1687         /// </summary>
HandleRegularRecord(RecordColumnMap columnMap, TranslatorArg arg, RowType spanRowType)1688         private Expression HandleRegularRecord(RecordColumnMap columnMap, TranslatorArg arg, RowType spanRowType)
1689         {
1690             // handle regular records
1691 
1692             // Build an array of expressions that read the individual values from the
1693             // source data reader.
1694             Expression[] columnReaders = new Expression[columnMap.Properties.Length];
1695             for (int i = 0; i < columnReaders.Length; i++)
1696             {
1697                 Expression columnReader = AcceptWithMappedType(this, columnMap.Properties[i], columnMap).UnwrappedExpression;
1698 
1699                 // ((object)columnReader) ?? DBNull.Value
1700                 columnReaders[i] = Expression.Coalesce(Emit_EnsureType(columnReader, typeof(object)), DBNull_Value);
1701             }
1702             // new object[] {columnReader0..columnReaderN}
1703             Expression columnReaderArray = Expression.NewArrayInit(typeof(object), columnReaders);
1704 
1705 
1706             // Get an expression representing the TypeUsage of the MaterializedDataRecord
1707             // we're about to construct; we need to remove the span information from it,
1708             // though, since we don't want to surface that...
1709             TypeUsage type = columnMap.Type;
1710             if (null != _spanIndex)
1711             {
1712                 type = _spanIndex.GetSpannedRowType(spanRowType) ?? type;
1713             }
1714             Expression typeUsage = Expression.Constant(type, typeof(TypeUsage));
1715 
1716             // new MaterializedDataRecord(Shaper.Workspace, typeUsage, values)
1717             Expression result = Emit_EnsureType(Expression.New(MaterializedDataRecord_ctor, Shaper_Workspace, typeUsage, columnReaderArray), arg.RequestedType);
1718             return result;
1719         }
1720 
1721         /// <summary>
1722         /// Build expression to materialize the spanned information
1723         /// </summary>
HandleSpandexRecord(RecordColumnMap columnMap, TranslatorArg arg, RowType spanRowType)1724         private Expression HandleSpandexRecord(RecordColumnMap columnMap, TranslatorArg arg, RowType spanRowType)
1725         {
1726             Dictionary<int, AssociationEndMember> spanMap = _spanIndex.GetSpanMap(spanRowType);
1727 
1728             // First, build the expression to materialize the root item.
1729             Expression result = columnMap.Properties[0].Accept(this, arg).Expression;
1730 
1731             // Now build expressions that call into the appropriate shaper method
1732             // for the type of span for each spanned item.
1733             for (int i = 1; i < columnMap.Properties.Length; i++)
1734             {
1735                 AssociationEndMember targetMember = spanMap[i];
1736                 TranslatorResult propertyTranslatorResult = AcceptWithMappedType(this, columnMap.Properties[i], columnMap);
1737                 Expression spannedResultReader = propertyTranslatorResult.Expression;
1738 
1739                 // figure out the flavor of the span
1740                 CollectionTranslatorResult collectionTranslatorResult = propertyTranslatorResult as CollectionTranslatorResult;
1741                 if (null != collectionTranslatorResult)
1742                 {
1743                     Expression expressionToGetCoordinator = collectionTranslatorResult.ExpressionToGetCoordinator;
1744 
1745                     // full span collection
1746                     Type elementType = spannedResultReader.Type.GetGenericArguments()[0];
1747 
1748                     MethodInfo handleFullSpanCollectionMethod = Shaper_HandleFullSpanCollection.MakeGenericMethod(arg.RequestedType, elementType);
1749                     result = Expression.Call(Shaper_Parameter, handleFullSpanCollectionMethod, result, expressionToGetCoordinator, Expression.Constant(targetMember));
1750                 }
1751                 else
1752                 {
1753                     if (typeof(EntityKey) == spannedResultReader.Type)
1754                     {
1755                         // relationship span
1756                         MethodInfo handleRelationshipSpanMethod = Shaper_HandleRelationshipSpan.MakeGenericMethod(arg.RequestedType);
1757                         result = Expression.Call(Shaper_Parameter, handleRelationshipSpanMethod, result, spannedResultReader, Expression.Constant(targetMember));
1758                     }
1759                     else
1760                     {
1761                         // full span element
1762                         MethodInfo handleFullSpanElementMethod = Shaper_HandleFullSpanElement.MakeGenericMethod(arg.RequestedType, spannedResultReader.Type);
1763                         result = Expression.Call(Shaper_Parameter, handleFullSpanElementMethod, result, spannedResultReader, Expression.Constant(targetMember));
1764                     }
1765                 }
1766             }
1767             return result;
1768         }
1769 
1770         #endregion
1771 
1772         #region collection columns
1773 
1774         /// <summary>
1775         /// Visit(SimpleCollectionColumnMap)
1776         /// </summary>
Visit(SimpleCollectionColumnMap columnMap, TranslatorArg arg)1777         internal override TranslatorResult Visit(SimpleCollectionColumnMap columnMap, TranslatorArg arg)
1778         {
1779             return ProcessCollectionColumnMap(columnMap, arg);
1780         }
1781 
1782         /// <summary>
1783         /// Visit(DiscriminatedCollectionColumnMap)
1784         /// </summary>
Visit(DiscriminatedCollectionColumnMap columnMap, TranslatorArg arg)1785         internal override TranslatorResult Visit(DiscriminatedCollectionColumnMap columnMap, TranslatorArg arg)
1786         {
1787             return ProcessCollectionColumnMap(columnMap, arg, columnMap.Discriminator, columnMap.DiscriminatorValue);
1788         }
1789 
1790         /// <summary>
1791         /// Common code for both Simple and Discrminated Column Maps.
1792         /// </summary>
ProcessCollectionColumnMap(CollectionColumnMap columnMap, TranslatorArg arg)1793         private TranslatorResult ProcessCollectionColumnMap(CollectionColumnMap columnMap, TranslatorArg arg)
1794         {
1795             return ProcessCollectionColumnMap(columnMap, arg, null, null);
1796         }
1797 
1798         /// <summary>
1799         /// Common code for both Simple and Discrminated Column Maps.
1800         /// </summary>
ProcessCollectionColumnMap(CollectionColumnMap columnMap, TranslatorArg arg, ColumnMap discriminatorColumnMap, object discriminatorValue)1801         private TranslatorResult ProcessCollectionColumnMap(CollectionColumnMap columnMap, TranslatorArg arg, ColumnMap discriminatorColumnMap, object discriminatorValue)
1802         {
1803             Type elementType = DetermineElementType(arg.RequestedType, columnMap);
1804 
1805             // CoordinatorScratchpad aggregates information about the current nested
1806             // result (represented by the given CollectionColumnMap)
1807             CoordinatorScratchpad coordinatorScratchpad = new CoordinatorScratchpad(elementType);
1808 
1809             // enter scope for current coordinator when translating children, etc.
1810             EnterCoordinatorTranslateScope(coordinatorScratchpad);
1811 
1812 
1813             ColumnMap elementColumnMap = columnMap.Element;
1814 
1815             if (IsValueLayer)
1816             {
1817                 StructuredColumnMap structuredElement = elementColumnMap as StructuredColumnMap;
1818 
1819                 // If we have a collection of non-structured types we have to put
1820                 // a structure around it, because we don't have data readers of
1821                 // scalars, only structures.  We don't need a null sentinel because
1822                 // this structure can't ever be null.
1823                 if (null == structuredElement)
1824                 {
1825                     ColumnMap[] columnMaps = new ColumnMap[1] { columnMap.Element };
1826                     elementColumnMap = new RecordColumnMap(columnMap.Element.Type, columnMap.Element.Name, columnMaps, null);
1827                 }
1828             }
1829 
1830             // Build the expression that will construct the element of the collection
1831             // from the source data reader.
1832             // We use UnconvertedExpression here so we can defer doing type checking in case
1833             // we need to translate to a POCO collection later in the process.
1834             Expression elementReader = elementColumnMap.Accept(this, new TranslatorArg(elementType)).UnconvertedExpression;
1835 
1836             // Build the expression(s) that read the collection's keys from the source
1837             // data reader; note that the top level collection may not have keys if there
1838             // are no children.
1839             Expression[] keyReaders;
1840 
1841             if (null != columnMap.Keys)
1842             {
1843                 keyReaders = new Expression[columnMap.Keys.Length];
1844                 for (int i = 0; i < keyReaders.Length; i++)
1845                 {
1846                     Expression keyReader = AcceptWithMappedType(this, columnMap.Keys[i], columnMap).Expression;
1847                     keyReaders[i] = keyReader;
1848                 }
1849             }
1850             else
1851             {
1852                 keyReaders = new Expression[] { };
1853             }
1854 
1855             // Build the expression that reads the discriminator value from the source
1856             // data reader.
1857             Expression discriminatorReader = null;
1858             if (null != discriminatorColumnMap)
1859             {
1860                 discriminatorReader = AcceptWithMappedType(this, discriminatorColumnMap, columnMap).Expression;
1861             }
1862 
1863             // get expression retrieving the coordinator
1864             Expression expressionToGetCoordinator = BuildExpressionToGetCoordinator(elementType, elementReader, keyReaders, discriminatorReader, discriminatorValue, coordinatorScratchpad);
1865             MethodInfo getElementsExpression = typeof(Coordinator<>).MakeGenericType(elementType).GetMethod("GetElements", BindingFlags.NonPublic | BindingFlags.Instance);
1866 
1867             Expression result;
1868             if (IsValueLayer)
1869             {
1870                 result = expressionToGetCoordinator;
1871             }
1872             else
1873             {
1874                 // coordinator.GetElements()
1875                 result = Expression.Call(expressionToGetCoordinator, getElementsExpression);
1876 
1877                 // Perform the type check that was previously deferred so we could process POCO collections.
1878                 coordinatorScratchpad.Element = Emit_EnsureType(coordinatorScratchpad.Element, elementType);
1879 
1880                 // When materializing specifically requested collection types, we need
1881                 // to transfer the results from the Enumerable to the requested collection.
1882                 Type innerElementType;
1883                 if (EntityUtil.TryGetICollectionElementType(arg.RequestedType, out innerElementType))
1884                 {
1885                     // Given we have some type that implements ICollection<T>, we need to decide what concrete
1886                     // collection type to instantiate--See EntityUtil.DetermineCollectionType for details.
1887                     var typeToInstantiate = EntityUtil.DetermineCollectionType(arg.RequestedType);
1888 
1889                     if (typeToInstantiate == null)
1890                     {
1891                         throw EntityUtil.InvalidOperation(Strings.ObjectQuery_UnableToMaterializeArbitaryProjectionType(arg.RequestedType));
1892                     }
1893 
1894                     Type listOfElementType = typeof(List<>).MakeGenericType(innerElementType);
1895                     if (typeToInstantiate != listOfElementType)
1896                     {
1897                         coordinatorScratchpad.InitializeCollection = Emit_EnsureType(
1898                             Expression.New(GetConstructor(typeToInstantiate)),
1899                             typeof(ICollection<>).MakeGenericType(innerElementType));
1900                     }
1901                     result = Emit_EnsureType(result, arg.RequestedType);
1902                 }
1903                 else
1904                 {
1905                     // If any compensation is required (returning IOrderedEnumerable<T>, not
1906                     // just vanilla IEnumerable<T> we must wrap the result with a static class
1907                     // that is of the type expected.
1908                     if (!arg.RequestedType.IsAssignableFrom(result.Type))
1909                     {
1910                         // new CompensatingCollection<TElement>(_collectionReader)
1911                         Type compensatingCollectionType = typeof(CompensatingCollection<>).MakeGenericType(elementType);
1912                         ConstructorInfo constructorInfo = compensatingCollectionType.GetConstructors()[0];
1913                         result = Emit_EnsureType(Expression.New(constructorInfo, result), compensatingCollectionType);
1914                     }
1915                 }
1916             }
1917             ExitCoordinatorTranslateScope();
1918             return new CollectionTranslatorResult(result, columnMap, arg.RequestedType, expressionToGetCoordinator);
1919         }
1920 
1921         /// <summary>
1922         /// Returns the CLR Type of the element of the collection
1923         /// </summary>
DetermineElementType(Type collectionType, CollectionColumnMap columnMap)1924         private Type DetermineElementType(Type collectionType, CollectionColumnMap columnMap)
1925         {
1926             Type result = null;
1927 
1928             if (IsValueLayer)
1929             {
1930                 result = typeof(RecordState);
1931             }
1932             else
1933             {
1934                 result = TypeSystem.GetElementType(collectionType);
1935 
1936                 // GetElementType returns the input type if it is not a collection.
1937                 if (result == collectionType)
1938                 {
1939                     // if the user isn't asking for a CLR collection type (e.g. ObjectQuery<object>("{{1, 2}}")), we choose for them
1940                     TypeUsage edmElementType = ((CollectionType)columnMap.Type.EdmType).TypeUsage; // the TypeUsage of the Element of the collection.
1941                     result = DetermineClrType(edmElementType);
1942                 }
1943             }
1944             return result;
1945         }
1946 
1947         /// <summary>
1948         /// Build up the coordinator graph using Enter/ExitCoordinatorTranslateScope.
1949         /// </summary>
EnterCoordinatorTranslateScope(CoordinatorScratchpad coordinatorScratchpad)1950         private void EnterCoordinatorTranslateScope(CoordinatorScratchpad coordinatorScratchpad)
1951         {
1952             if (null == _rootCoordinatorScratchpad)
1953             {
1954                 coordinatorScratchpad.Depth = 0;
1955                 _rootCoordinatorScratchpad = coordinatorScratchpad;
1956                 _currentCoordinatorScratchpad = coordinatorScratchpad;
1957             }
1958             else
1959             {
1960                 coordinatorScratchpad.Depth = _currentCoordinatorScratchpad.Depth + 1;
1961                 _currentCoordinatorScratchpad.AddNestedCoordinator(coordinatorScratchpad);
1962                 _currentCoordinatorScratchpad = coordinatorScratchpad;
1963             }
1964         }
1965 
ExitCoordinatorTranslateScope()1966         private void ExitCoordinatorTranslateScope()
1967         {
1968             _currentCoordinatorScratchpad = _currentCoordinatorScratchpad.Parent;
1969         }
1970 
1971         /// <summary>
1972         /// Return an expression to read the coordinator from a state slot at
1973         /// runtime.  This is the method where we store the expressions we've
1974         /// been building into the CoordinatorScratchpad, which we'll compile
1975         /// later, once we've left the visitor.
1976         /// </summary>
BuildExpressionToGetCoordinator(Type elementType, Expression element, Expression[] keyReaders, Expression discriminator, object discriminatorValue, CoordinatorScratchpad coordinatorScratchpad)1977         private Expression BuildExpressionToGetCoordinator(Type elementType, Expression element, Expression[] keyReaders, Expression discriminator, object discriminatorValue, CoordinatorScratchpad coordinatorScratchpad)
1978         {
1979             int stateSlotNumber = AllocateStateSlot();
1980             coordinatorScratchpad.StateSlotNumber = stateSlotNumber;
1981 
1982             // Ensure that the element type of the collec element translator
1983             coordinatorScratchpad.Element = element;
1984 
1985             // Build expressions to set the key values into their state slots, and
1986             // to compare the current values from the source reader with the values
1987             // in the slots.
1988             List<Expression> setKeyTerms = new List<Expression>(keyReaders.Length);
1989             List<Expression> checkKeyTerms = new List<Expression>(keyReaders.Length);
1990 
1991             foreach (Expression keyReader in keyReaders)
1992             {
1993                 // allocate space for the key value in the reader state
1994                 int keyStateSlotNumber = AllocateStateSlot();
1995 
1996                 // SetKey: readerState.SetState<T>(stateSlot, keyReader)
1997                 setKeyTerms.Add(Emit_Shaper_SetState(keyStateSlotNumber, keyReader));
1998 
1999                 // CheckKey: ((T)readerState.State[ordinal]).Equals(keyValue)
2000                 checkKeyTerms.Add(Emit_Equal(
2001                                         Emit_Shaper_GetState(keyStateSlotNumber, keyReader.Type),
2002                                         keyReader
2003                                         )
2004                                  );
2005             }
2006 
2007             // For setting keys, we use BitwiseOr so that we don't short-circuit (all
2008             // key terms are set)
2009             coordinatorScratchpad.SetKeys = Emit_BitwiseOr(setKeyTerms);
2010 
2011             // When checking for equality, we use AndAlso so that we short-circuit (return
2012             // as soon as key values don't match)
2013             coordinatorScratchpad.CheckKeys = Emit_AndAlso(checkKeyTerms);
2014 
2015             if (null != discriminator)
2016             {
2017                 // discriminatorValue == discriminator
2018                 coordinatorScratchpad.HasData = Emit_Equal(
2019                                                     Expression.Constant(discriminatorValue, discriminator.Type),
2020                                                     discriminator
2021                                                     );
2022             }
2023 
2024             // Finally, build the expression to read the coordinator from the state
2025             // (Coordinator<elementType>)readerState.State[stateOrdinal]
2026             Expression result = Emit_Shaper_GetState(stateSlotNumber, typeof(Coordinator<>).MakeGenericType(elementType));
2027             return result;
2028         }
2029 
2030         #endregion
2031 
2032         #region "scalar" columns
2033 
2034         /// <summary>
2035         /// Visit(RefColumnMap)
2036         ///
2037         /// If the entityKey has a value, then return it otherwise return a null
2038         /// valued EntityKey.  The EntityKey construction is the tricky part.
2039         /// </summary>
Visit(RefColumnMap columnMap, TranslatorArg arg)2040         internal override TranslatorResult Visit(RefColumnMap columnMap, TranslatorArg arg)
2041         {
2042             EntityIdentity entityIdentity = columnMap.EntityIdentity;
2043             Expression entitySetReader; // Ignored here; used when constructing Entities
2044 
2045             // hasValue ? entityKey : (EntityKey)null
2046             Expression result = Expression.Condition(
2047                                                 Emit_EntityKey_HasValue(entityIdentity.Keys),
2048                                                 Emit_EntityKey_ctor(this, entityIdentity, true, out entitySetReader),
2049                                                 Expression.Constant(null, typeof(EntityKey))
2050                                                 );
2051             return new TranslatorResult(result, arg.RequestedType);
2052         }
2053 
2054         /// <summary>
2055         /// Visit(ScalarColumnMap)
2056         ///
2057         /// Pretty basic stuff here; we just call the method that matches the
2058         /// type of the column.  Of course we have to handle nullable/non-nullable
2059         /// types, and non-value types.
2060         /// </summary>
Visit(ScalarColumnMap columnMap, TranslatorArg arg)2061         internal override TranslatorResult Visit(ScalarColumnMap columnMap, TranslatorArg arg)
2062         {
2063             Type type = arg.RequestedType;
2064             TypeUsage columnType = columnMap.Type;
2065             int ordinal = columnMap.ColumnPos;
2066             Expression result;
2067 
2068             // 1. Create an expression to access the column value as an instance of the correct type. For non-spatial types this requires a call to one of the
2069             //    DbDataReader GetXXX methods; spatial values must be read using the provider's spatial services implementation.
2070             // 2. If the type was nullable (strings, byte[], Nullable<T>), wrap the expression with a check for the DBNull value and produce the correct typed null instead.
2071             //    Since the base spatial types (DbGeography/DbGeometry) are reference types, this is always required for spatial columns.
2072             // 3. Also create a version of the expression with error handling so that we can throw better exception messages when needed
2073             //
2074             PrimitiveTypeKind typeKind;
2075             if (Helper.IsSpatialType(columnType, out typeKind))
2076             {
2077                 Debug.Assert(Helper.IsGeographicType((PrimitiveType)columnType.EdmType) || Helper.IsGeometricType((PrimitiveType)columnType.EdmType), "Spatial primitive type is neither Geometry or Geography?");
2078                 result = Emit_Conditional_NotDBNull(Helper.IsGeographicType((PrimitiveType)columnType.EdmType) ? Emit_EnsureType(Emit_Shaper_GetGeographyColumnValue(ordinal), type)
2079                                                                                             : Emit_EnsureType(Emit_Shaper_GetGeometryColumnValue(ordinal), type),
2080                                                     ordinal, type);
2081             }
2082             else
2083             {
2084                 bool needsNullableCheck;
2085                 MethodInfo readerMethod = GetReaderMethod(type, out needsNullableCheck);
2086 
2087                 result = Expression.Call(Shaper_Reader, readerMethod, Expression.Constant(ordinal));
2088 
2089                 // if the requested type is a nullable enum we need to cast it first to the non-nullable enum type to avoid InvalidCastException.
2090                 // Note that we guard against null values by wrapping the expression with DbNullCheck later. Also we don't actually
2091                 // look at the type of the value returned by reader. If the value is not castable to enum we will fail with cast exception.
2092                 Type nonNullableType = TypeSystem.GetNonNullableType(type);
2093                 if (nonNullableType.IsEnum && nonNullableType != type)
2094                 {
2095                     Debug.Assert(needsNullableCheck, "This is a nullable enum so needsNullableCheck should be true to emit code that handles null values read from the reader.");
2096 
2097                     result = Expression.Convert(result, nonNullableType);
2098                 }
2099                 else if(type == typeof(object))
2100                 {
2101                     Debug.Assert(!needsNullableCheck, "If the requested type is object there is no special handling for null values returned from the reader.");
2102 
2103                     // special case for an OSpace query where the requested type is object but the column type is of an enum type. In this case
2104                     // we want to return a boxed value of enum type instead a boxed value of the enum underlying type. We also need to handle null
2105                     // values to return DBNull to be consistent with behavior for primitive types (e.g. int)
2106                     if (!IsValueLayer && TypeSemantics.IsEnumerationType(columnType))
2107                     {
2108                         result = Expression.Condition(
2109                                     Emit_Reader_IsDBNull(ordinal),
2110                                         result,
2111                                         Expression.Convert(Expression.Convert(result, TypeSystem.GetNonNullableType(DetermineClrType(columnType.EdmType))), typeof(object)));
2112                     }
2113                 }
2114 
2115                 // (type)shaper.Reader.Get???(ordinal)
2116                 result = Emit_EnsureType(result, type);
2117 
2118                 if (needsNullableCheck)
2119                 {
2120                     result = Emit_Conditional_NotDBNull(result, ordinal, type);
2121                 }
2122             }
2123 
2124             Expression resultWithErrorHandling = Emit_Shaper_GetColumnValueWithErrorHandling(arg.RequestedType, ordinal, columnType);
2125             _currentCoordinatorScratchpad.AddExpressionWithErrorHandling(result, resultWithErrorHandling);
2126             return new TranslatorResult(result, type);
2127         }
2128 
Emit_Conditional_NotDBNull(Expression result, int ordinal, Type columnType)2129         private static Expression Emit_Conditional_NotDBNull(Expression result, int ordinal, Type columnType)
2130         {
2131             result = Expression.Condition(Emit_Reader_IsDBNull(ordinal),
2132                                           Expression.Constant(TypeSystem.GetDefaultValue(columnType), columnType),
2133                                           result);
2134             return result;
2135         }
2136 
GetReaderMethod(Type type, out bool isNullable)2137         internal static MethodInfo GetReaderMethod(Type type, out bool isNullable)
2138         {
2139             Debug.Assert(null != type, "type required");
2140 
2141             MethodInfo result;
2142             isNullable = false;
2143 
2144             // determine if this is a Nullable<T>
2145             Type underlyingType = Nullable.GetUnderlyingType(type);
2146             if (null != underlyingType)
2147             {
2148                 isNullable = true;
2149                 type = underlyingType;
2150             }
2151 
2152             TypeCode typeCode = Type.GetTypeCode(type);
2153 
2154             switch (typeCode)
2155             {
2156                 case TypeCode.String:
2157                     result = DbDataReader_GetString;
2158                     isNullable = true;
2159                     break;
2160                 case TypeCode.Int16:
2161                     result = DbDataReader_GetInt16;
2162                     break;
2163                 case TypeCode.Int32:
2164                     result = DbDataReader_GetInt32;
2165                     break;
2166                 case TypeCode.Int64:
2167                     result = DbDataReader_GetInt64;
2168                     break;
2169                 case TypeCode.Boolean:
2170                     result = DbDataReader_GetBoolean;
2171                     break;
2172                 case TypeCode.Decimal:
2173                     result = DbDataReader_GetDecimal;
2174                     break;
2175                 case TypeCode.Double:
2176                     result = DbDataReader_GetDouble;
2177                     break;
2178                 case TypeCode.Single:
2179                     result = DbDataReader_GetFloat;
2180                     break;
2181                 case TypeCode.DateTime:
2182                     result = DbDataReader_GetDateTime;
2183                     break;
2184                 case TypeCode.Byte:
2185                     result = DbDataReader_GetByte;
2186                     break;
2187                 default:
2188                     if (typeof(Guid) == type)
2189                     {
2190                         // Guid doesn't have a type code
2191                         result = DbDataReader_GetGuid;
2192                     }
2193                     else if (typeof(TimeSpan) == type ||
2194                              typeof(DateTimeOffset) == type)
2195                     {
2196                         // TimeSpan and DateTimeOffset don't have a type code or a specific
2197                         // GetXXX method
2198                         result = DbDataReader_GetValue;
2199                     }
2200                     else if (typeof(Object) == type)
2201                     {
2202                         // We assume that Object means we want DBNull rather than null. I believe this is a bug.
2203                         result = DbDataReader_GetValue;
2204                     }
2205                     else
2206                     {
2207                         result = DbDataReader_GetValue;
2208                         isNullable = true;
2209                     }
2210                     break;
2211             }
2212             return result;
2213         }
2214 
2215         /// <summary>
2216         /// Visit(VarRefColumnMap)
2217         ///
2218         /// This should throw; VarRefColumnMaps should be removed by the PlanCompiler.
2219         /// </summary>
Visit(VarRefColumnMap columnMap, TranslatorArg arg)2220         internal override TranslatorResult Visit(VarRefColumnMap columnMap, TranslatorArg arg)
2221         {
2222             Debug.Fail("VarRefColumnMap should be substituted at this point");
2223             throw EntityUtil.InvalidOperation(String.Empty);
2224         }
2225 
2226         #endregion
2227 
2228         #endregion
2229     }
2230 }
2231