1 //--------------------------------------------------------------------- 2 // <copyright file="InitializerFacet.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // 6 // @owner Microsoft 7 //--------------------------------------------------------------------- 8 9 namespace System.Data.Objects.ELinq 10 { 11 using System.Collections.Generic; 12 using System.Data.Common; 13 using System.Data.Common.Internal.Materialization; 14 using System.Data.Metadata.Edm; 15 using System.Data.Objects.DataClasses; 16 using System.Data.Objects.Internal; 17 using System.Diagnostics; 18 using System.Globalization; 19 using System.Linq; 20 using System.Linq.Expressions; 21 using System.Reflection; 22 using System.Security; 23 using System.Security.Permissions; 24 using System.Threading; 25 26 /// <summary> 27 /// Facet encapsulating information necessary to initialize a LINQ projection 28 /// result. 29 /// </summary> 30 internal abstract class InitializerMetadata : IEquatable<InitializerMetadata> 31 { 32 internal readonly Type ClrType; 33 internal static readonly MethodInfo UserExpressionMarker = typeof(InitializerMetadata).GetMethod("MarkAsUserExpression", BindingFlags.NonPublic | BindingFlags.Static); 34 private static long s_identifier; 35 internal readonly string Identity; 36 private static readonly string s_identifierPrefix = typeof(InitializerMetadata).Name; 37 InitializerMetadata(Type clrType)38 private InitializerMetadata(Type clrType) 39 { 40 Debug.Assert(null != clrType); 41 ClrType = clrType; 42 Identity = s_identifierPrefix + Interlocked.Increment(ref s_identifier).ToString(CultureInfo.InvariantCulture); 43 } 44 45 // Gets the kind of this initializer (grouping, row, etc.) 46 internal abstract InitializerMetadataKind Kind { get; } 47 48 // Attempts to retrieve the initializer facet from a type usage TryGetInitializerMetadata(TypeUsage typeUsage, out InitializerMetadata initializerMetadata)49 internal static bool TryGetInitializerMetadata(TypeUsage typeUsage, out InitializerMetadata initializerMetadata) 50 { 51 initializerMetadata = null; 52 if (BuiltInTypeKind.RowType == typeUsage.EdmType.BuiltInTypeKind) 53 { 54 initializerMetadata = ((RowType)typeUsage.EdmType).InitializerMetadata; 55 } 56 return null != initializerMetadata; 57 } 58 59 // Initializes an initializer for an IGrouping return type 60 // Requires: resultType is IGrouping<T, K> instance. CreateGroupingInitializer(EdmItemCollection itemCollection, Type resultType)61 internal static InitializerMetadata CreateGroupingInitializer(EdmItemCollection itemCollection, Type resultType) 62 { 63 return itemCollection.GetCanonicalInitializerMetadata(new GroupingInitializerMetadata(resultType)); 64 } 65 66 // Initializes an initializer for a MemberInit expression CreateProjectionInitializer(EdmItemCollection itemCollection, MemberInitExpression initExpression, MemberInfo[] members)67 internal static InitializerMetadata CreateProjectionInitializer(EdmItemCollection itemCollection, MemberInitExpression initExpression, 68 MemberInfo[] members) 69 { 70 return itemCollection.GetCanonicalInitializerMetadata(new ProjectionInitializerMetadata(initExpression, members)); 71 } 72 73 // Initializes an initializer for a New expression CreateProjectionInitializer(EdmItemCollection itemCollection, NewExpression newExpression)74 internal static InitializerMetadata CreateProjectionInitializer(EdmItemCollection itemCollection, NewExpression newExpression) 75 { 76 return itemCollection.GetCanonicalInitializerMetadata(new ProjectionNewMetadata(newExpression)); 77 } 78 79 // Initializes an initializer for a New expression with no properties CreateEmptyProjectionInitializer(EdmItemCollection itemCollection, NewExpression newExpression)80 internal static InitializerMetadata CreateEmptyProjectionInitializer(EdmItemCollection itemCollection, NewExpression newExpression) 81 { 82 return itemCollection.GetCanonicalInitializerMetadata(new EmptyProjectionNewMetadata(newExpression)); 83 } 84 85 // Creates metadata for entity collection materialization CreateEntityCollectionInitializer(EdmItemCollection itemCollection, Type type, NavigationProperty navigationProperty)86 internal static InitializerMetadata CreateEntityCollectionInitializer(EdmItemCollection itemCollection, Type type, NavigationProperty navigationProperty) 87 { 88 return itemCollection.GetCanonicalInitializerMetadata(new EntityCollectionInitializerMetadata(type, navigationProperty)); 89 } 90 MarkAsUserExpression(T value)91 private static T MarkAsUserExpression<T>(T value) 92 { 93 // No op. This is used as a marker inside of an expression tree to indicate 94 // that the input expression is not trusted. 95 return value; 96 } 97 AppendColumnMapKey(ColumnMapKeyBuilder builder)98 internal virtual void AppendColumnMapKey(ColumnMapKeyBuilder builder) 99 { 100 // by default, the type is sufficient (more information is needed for EntityCollection and initializers) 101 builder.Append("CLR-", this.ClrType); 102 } 103 Equals(object obj)104 public override bool Equals(object obj) 105 { 106 Debug.Fail("use typed Equals method only"); 107 return Equals(obj as InitializerMetadata); 108 } 109 Equals(InitializerMetadata other)110 public bool Equals(InitializerMetadata other) 111 { 112 Debug.Assert(null != other, "must not use a null key"); 113 if (object.ReferenceEquals(this, other)) { return true; } 114 if (this.Kind != other.Kind) { return false; } 115 if (!this.ClrType.Equals(other.ClrType)) { return false; } 116 return IsStructurallyEquivalent(other); 117 } 118 119 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2303", Justification="ClrType is not expected to be an Embedded Interop Type.")] GetHashCode()120 public override int GetHashCode() 121 { 122 return ClrType.GetHashCode(); 123 } 124 125 /// <summary> 126 /// Requires: other has the same type as this and refers to the same CLR type 127 /// Determine whether this Metadata is compatible with the other based on record layout. 128 /// </summary> IsStructurallyEquivalent(InitializerMetadata other)129 protected virtual bool IsStructurallyEquivalent(InitializerMetadata other) 130 { 131 return true; 132 } 133 134 /// <summary> 135 /// Produces an expression initializing an instance of ClrType (given emitters for input 136 /// columns) 137 /// </summary> Emit(Translator translator, List<TranslatorResult> propertyTranslatorResults)138 internal abstract Expression Emit(Translator translator, List<TranslatorResult> propertyTranslatorResults); 139 140 /// <summary> 141 /// Yields expected types for input columns. Null values are returned for children 142 /// whose type is irrelevant to the initializer. 143 /// </summary> GetChildTypes()144 internal abstract IEnumerable<Type> GetChildTypes(); 145 146 /// <summary> 147 /// return a list of propertyReader expressions from an array of translator results. 148 /// </summary> 149 /// <param name="propertyTranslatorResults"></param> 150 /// <returns></returns> GetPropertyReaders(List<TranslatorResult> propertyTranslatorResults)151 protected static List<Expression> GetPropertyReaders(List<TranslatorResult> propertyTranslatorResults) 152 { 153 List<Expression> propertyReaders = propertyTranslatorResults.Select(s => s.UnwrappedExpression).ToList(); 154 return propertyReaders; 155 } 156 157 /// <summary> 158 /// Implementation of IGrouping that can be initialized using the standard 159 /// initializer pattern supported by ELinq 160 /// </summary> 161 /// <typeparam name="K">Type of key</typeparam> 162 /// <typeparam name="T">Type of record</typeparam> 163 private class Grouping<K, T> : IGrouping<K, T> 164 { Grouping(K key, IEnumerable<T> group)165 public Grouping(K key, IEnumerable<T> group) 166 { 167 _key = key; 168 _group = group; 169 } 170 171 private readonly K _key; 172 private readonly IEnumerable<T> _group; 173 174 public K Key 175 { 176 get { return _key; } 177 } 178 179 public IEnumerable<T> Group 180 { 181 get { return _group; } 182 } 183 GetEnumerator()184 IEnumerator<T> IEnumerable<T>.GetEnumerator() 185 { 186 if (null == _group) 187 { 188 yield break; 189 } 190 foreach (T member in _group) 191 { 192 yield return member; 193 } 194 } 195 System.Collections.IEnumerable.GetEnumerator()196 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 197 { 198 return ((IEnumerable<T>)this).GetEnumerator(); 199 } 200 } 201 202 /// <summary> 203 /// Metadata for grouping initializer. 204 /// </summary> 205 private class GroupingInitializerMetadata : InitializerMetadata 206 { GroupingInitializerMetadata(Type type)207 internal GroupingInitializerMetadata(Type type) 208 : base(type) 209 { 210 } 211 212 internal override InitializerMetadataKind Kind { get { return InitializerMetadataKind.Grouping; } } 213 Emit(Translator translator, List<TranslatorResult> propertyTranslatorResults)214 internal override Expression Emit(Translator translator, List<TranslatorResult> propertyTranslatorResults) 215 { 216 // Create expression of the form: 217 // new Grouping<K, T>(children[0], children[1]) 218 219 // Collect information... 220 Debug.Assert(ClrType.IsGenericType && 221 typeof(IGrouping<,>).Equals(ClrType.GetGenericTypeDefinition())); 222 Debug.Assert(propertyTranslatorResults.Count == 2); 223 Type keyType = this.ClrType.GetGenericArguments()[0]; 224 Type groupElementType = this.ClrType.GetGenericArguments()[1]; 225 Type groupType = typeof(Grouping<,>).MakeGenericType(keyType, groupElementType); 226 ConstructorInfo constructor = groupType.GetConstructors().Single(); 227 228 // new Grouping<K, T>(children[0], children[1]) 229 Expression newGrouping = Expression.Convert(Expression.New(constructor, GetPropertyReaders(propertyTranslatorResults)), this.ClrType); 230 231 return newGrouping; 232 } 233 GetChildTypes()234 internal override IEnumerable<Type> GetChildTypes() 235 { 236 // Collect information... 237 Debug.Assert(ClrType.IsGenericType && 238 typeof(IGrouping<,>).Equals(ClrType.GetGenericTypeDefinition())); 239 Type keyType = this.ClrType.GetGenericArguments()[0]; 240 Type groupElementType = this.ClrType.GetGenericArguments()[1]; 241 242 // key 243 yield return keyType; 244 // group 245 yield return typeof(IEnumerable<>).MakeGenericType(groupElementType); 246 } 247 } 248 249 /// <summary> 250 /// Metadata for anonymous type materialization. 251 /// </summary> 252 private class ProjectionNewMetadata : InitializerMetadata 253 { ProjectionNewMetadata(NewExpression newExpression)254 internal ProjectionNewMetadata(NewExpression newExpression) 255 : base(newExpression.Type) 256 { 257 Debug.Assert(null != newExpression); 258 _newExpression = newExpression; 259 } 260 261 private readonly NewExpression _newExpression; 262 263 internal override InitializerMetadataKind Kind { get { return InitializerMetadataKind.ProjectionNew; } } 264 IsStructurallyEquivalent(InitializerMetadata other)265 protected override bool IsStructurallyEquivalent(InitializerMetadata other) 266 { 267 // caller must ensure the type matches 268 ProjectionNewMetadata otherProjection = (ProjectionNewMetadata)other; 269 if (this._newExpression.Members == null && otherProjection._newExpression.Members == null) 270 { 271 return true; 272 } 273 274 if (this._newExpression.Members == null || otherProjection._newExpression.Members == null) 275 { 276 return false; 277 } 278 279 if (this._newExpression.Members.Count != otherProjection._newExpression.Members.Count) 280 { 281 return false; 282 } 283 284 for (int i = 0; i < this._newExpression.Members.Count; i++) 285 { 286 MemberInfo thisMember = this._newExpression.Members[i]; 287 MemberInfo otherMember = otherProjection._newExpression.Members[i]; 288 if (!thisMember.Equals(otherMember)) 289 { 290 return false; 291 } 292 } 293 294 return true; 295 } 296 Emit(Translator translator, List<TranslatorResult> propertyTranslatorResults)297 internal override Expression Emit(Translator translator, List<TranslatorResult> propertyTranslatorResults) 298 { 299 // Create expression of the form: 300 // _newExpression(children) 301 302 // (ClrType)null 303 Expression nullProjection = Expression.Constant(null, this.ClrType); 304 305 // _newExpression with members rebound 306 Expression newProjection = Expression.New(_newExpression.Constructor, GetPropertyReaders(propertyTranslatorResults)); 307 308 // Indicate that this expression is provided by the user and should not be trusted. 309 return Expression.Call(UserExpressionMarker.MakeGenericMethod(newProjection.Type), newProjection); 310 } 311 GetChildTypes()312 internal override IEnumerable<Type> GetChildTypes() 313 { 314 // return all argument types 315 return _newExpression.Arguments.Select(arg => arg.Type); 316 } 317 AppendColumnMapKey(ColumnMapKeyBuilder builder)318 internal override void AppendColumnMapKey(ColumnMapKeyBuilder builder) 319 { 320 base.AppendColumnMapKey(builder); 321 builder.Append(_newExpression.Constructor.ToString()); 322 foreach (var member in _newExpression.Members ?? Enumerable.Empty<MemberInfo>()) 323 { 324 builder.Append("DT", member.DeclaringType); 325 builder.Append("." + member.Name); 326 } 327 } 328 } 329 330 private class EmptyProjectionNewMetadata : ProjectionNewMetadata 331 { EmptyProjectionNewMetadata(NewExpression newExpression)332 internal EmptyProjectionNewMetadata(NewExpression newExpression) 333 : base(newExpression) 334 { 335 } Emit(Translator translator, List<TranslatorResult> propertyReaders)336 internal override Expression Emit(Translator translator, List<TranslatorResult> propertyReaders) 337 { 338 // ignore sentinel column 339 return base.Emit(translator, new List<TranslatorResult>()); 340 } GetChildTypes()341 internal override IEnumerable<Type> GetChildTypes() 342 { 343 // ignore sentinel column 344 yield return null; 345 } 346 } 347 348 /// <summary> 349 /// Metadata for standard projection initializers. 350 /// </summary> 351 private class ProjectionInitializerMetadata : InitializerMetadata 352 { ProjectionInitializerMetadata(MemberInitExpression initExpression, MemberInfo[] members)353 internal ProjectionInitializerMetadata(MemberInitExpression initExpression, MemberInfo[] members) 354 : base(initExpression.Type) 355 { 356 Debug.Assert(null != initExpression); 357 Debug.Assert(null != members); 358 _initExpression = initExpression; 359 _members = members; 360 } 361 362 private readonly MemberInitExpression _initExpression; 363 private readonly MemberInfo[] _members; 364 365 internal override InitializerMetadataKind Kind { get { return InitializerMetadataKind.ProjectionInitializer; } } 366 IsStructurallyEquivalent(InitializerMetadata other)367 protected override bool IsStructurallyEquivalent(InitializerMetadata other) 368 { 369 // caller must ensure the type matches 370 ProjectionInitializerMetadata otherProjection = (ProjectionInitializerMetadata)other; 371 if (this._initExpression.Bindings.Count != otherProjection._initExpression.Bindings.Count) 372 { 373 return false; 374 } 375 376 for (int i = 0; i < this._initExpression.Bindings.Count; i++) 377 { 378 MemberBinding thisBinding = this._initExpression.Bindings[i]; 379 MemberBinding otherBinding = otherProjection._initExpression.Bindings[i]; 380 if (!thisBinding.Member.Equals(otherBinding.Member)) 381 { 382 return false; 383 } 384 } 385 386 return true; 387 } 388 Emit(Translator translator, List<TranslatorResult> propertyReaders)389 internal override Expression Emit(Translator translator, List<TranslatorResult> propertyReaders) 390 { 391 // Create expression of the form: 392 // _initExpression(children) 393 394 // create member bindings (where values are taken from children) 395 MemberBinding[] memberBindings = new MemberBinding[_initExpression.Bindings.Count]; 396 MemberBinding[] constantMemberBindings = new MemberBinding[memberBindings.Length]; 397 for (int i = 0; i < memberBindings.Length; i++) 398 { 399 MemberBinding originalBinding = _initExpression.Bindings[i]; 400 Expression value = propertyReaders[i].UnwrappedExpression; 401 MemberBinding newBinding = Expression.Bind(originalBinding.Member, value); 402 MemberBinding constantBinding = Expression.Bind(originalBinding.Member, Expression.Constant( 403 TypeSystem.GetDefaultValue(value.Type), value.Type)); 404 memberBindings[i] = newBinding; 405 constantMemberBindings[i] = constantBinding; 406 } 407 408 Expression newProjection = Expression.MemberInit(_initExpression.NewExpression, memberBindings); 409 410 // Indicate that this expression is provided by the user and should not be trusted. 411 return Expression.Call(UserExpressionMarker.MakeGenericMethod(newProjection.Type), newProjection); 412 } 413 GetChildTypes()414 internal override IEnumerable<Type> GetChildTypes() 415 { 416 // return all argument types 417 foreach (var binding in _initExpression.Bindings) 418 { 419 // determine member type 420 Type memberType; 421 string name; 422 TypeSystem.PropertyOrField(binding.Member, out name, out memberType); 423 yield return memberType; 424 } 425 } 426 AppendColumnMapKey(ColumnMapKeyBuilder builder)427 internal override void AppendColumnMapKey(ColumnMapKeyBuilder builder) 428 { 429 base.AppendColumnMapKey(builder); 430 foreach (var binding in _initExpression.Bindings) 431 { 432 builder.Append(",", binding.Member.DeclaringType); 433 builder.Append("." + binding.Member.Name); 434 } 435 } 436 } 437 438 /// <summary> 439 /// Metadata for entity collection initializer. 440 /// </summary> 441 private class EntityCollectionInitializerMetadata : InitializerMetadata 442 { EntityCollectionInitializerMetadata(Type type, NavigationProperty navigationProperty)443 internal EntityCollectionInitializerMetadata(Type type, NavigationProperty navigationProperty) 444 : base(type) 445 { 446 Debug.Assert(null != navigationProperty); 447 _navigationProperty = navigationProperty; 448 } 449 450 private readonly NavigationProperty _navigationProperty; 451 452 internal override InitializerMetadataKind Kind { get { return InitializerMetadataKind.EntityCollection; } } 453 454 /// <summary> 455 /// Make sure the other metadata instance generates the same property 456 /// (otherwise, we get incorrect behavior where multiple nav props return 457 /// the same type) 458 /// </summary> IsStructurallyEquivalent(InitializerMetadata other)459 protected override bool IsStructurallyEquivalent(InitializerMetadata other) 460 { 461 // caller must ensure the type matches 462 EntityCollectionInitializerMetadata otherInitializer = (EntityCollectionInitializerMetadata)other; 463 return this._navigationProperty.Equals(otherInitializer._navigationProperty); 464 } 465 466 private static readonly MethodInfo s_createEntityCollectionMethod = typeof(EntityCollectionInitializerMetadata).GetMethod("CreateEntityCollection", 467 BindingFlags.Static | BindingFlags.Public); 468 Emit(Translator translator, List<TranslatorResult> propertyTranslatorResults)469 internal override Expression Emit(Translator translator, List<TranslatorResult> propertyTranslatorResults) 470 { 471 Debug.Assert(propertyTranslatorResults.Count > 1, "no properties?"); 472 Debug.Assert(propertyTranslatorResults[1] is CollectionTranslatorResult, "not a collection?"); 473 474 Type elementType = GetElementType(); 475 MethodInfo createEntityCollectionMethod = s_createEntityCollectionMethod.MakeGenericMethod(elementType); 476 477 Expression shaper = Translator.Shaper_Parameter; 478 Expression owner = propertyTranslatorResults[0].Expression; 479 480 CollectionTranslatorResult collectionResult = propertyTranslatorResults[1] as CollectionTranslatorResult; 481 482 Expression coordinator = collectionResult.ExpressionToGetCoordinator; 483 484 // CreateEntityCollection(shaper, owner, elements, relationshipName, targetRoleName) 485 Expression result = Expression.Call(createEntityCollectionMethod, 486 shaper, owner, coordinator, Expression.Constant(_navigationProperty.RelationshipType.FullName), Expression.Constant(_navigationProperty.ToEndMember.Name)); 487 488 return result; 489 } 490 491 public static EntityCollection<T> CreateEntityCollection<T>(Shaper state, IEntityWrapper wrappedOwner, Coordinator<T> coordinator, string relationshipName, string targetRoleName) 492 where T : class 493 { 494 if (null == wrappedOwner.Entity) 495 { 496 return null; 497 } 498 else 499 { 500 EntityCollection<T> result = wrappedOwner.RelationshipManager.GetRelatedCollection<T>(relationshipName, targetRoleName); 501 // register a handler for deferred loading (when the nested result has been consumed) 502 coordinator.RegisterCloseHandler((readerState, elements) => result.Load(elements, readerState.MergeOption)); 503 return result; 504 } 505 } 506 GetChildTypes()507 internal override IEnumerable<Type> GetChildTypes() 508 { 509 Type elementType = GetElementType(); 510 yield return null; // defer in determining entity type... 511 yield return typeof(IEnumerable<>).MakeGenericType(elementType); 512 } 513 514 AppendColumnMapKey(ColumnMapKeyBuilder builder)515 internal override void AppendColumnMapKey(ColumnMapKeyBuilder builder) 516 { 517 base.AppendColumnMapKey(builder); 518 builder.Append(",NP" + _navigationProperty.Name); 519 builder.Append(",AT", _navigationProperty.DeclaringType); 520 } 521 GetElementType()522 private Type GetElementType() 523 { 524 // POCO support requires that we allow ICollection<T> collections. This allows a POCO collection 525 // to be projected in a LINQ query. 526 Type elementType; 527 if (!EntityUtil.TryGetICollectionElementType(ClrType, out elementType)) 528 { 529 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ELinq_UnexpectedTypeForNavigationProperty( 530 _navigationProperty, 531 typeof(EntityCollection<>), typeof(ICollection<>), 532 ClrType)); 533 } 534 return elementType; 535 } 536 } 537 } 538 539 internal enum InitializerMetadataKind 540 { 541 Grouping, 542 ProjectionNew, 543 ProjectionInitializer, 544 EntityCollection, 545 } 546 } 547