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<Shaper, body.Type> 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