1 //--------------------------------------------------------------------- 2 // <copyright file="MetadataHelper.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // 6 // @owner Microsoft 7 // @backupOwner Microsoft 8 //--------------------------------------------------------------------- 9 10 11 using System.Collections.Generic; 12 using System.Data.Mapping; 13 using System.Data.Metadata.Edm; 14 using System.Data.Objects.ELinq; 15 using System.Diagnostics; 16 using System.Linq; 17 using System.Security.Cryptography; 18 19 namespace System.Data.Common.Utils 20 { 21 // Helper functions to get metadata information 22 internal static class MetadataHelper 23 { 24 /// <summary> 25 /// Returns an element type of the collection returned by the function import. 26 /// Returns false, if element type cannot be determined. 27 /// </summary> 28 internal static bool TryGetFunctionImportReturnType<T>(EdmFunction functionImport, int resultSetIndex, out T returnType) where T : EdmType 29 { 30 T resultType; 31 if (TryGetWrappedReturnEdmTypeFromFunctionImport<T>(functionImport, resultSetIndex, out resultType)) 32 { 33 if (typeof(EntityType).Equals(typeof(T)) && resultType is EntityType 34 || typeof(ComplexType).Equals(typeof(T)) && resultType is ComplexType 35 || typeof(StructuralType).Equals(typeof(T)) && resultType is StructuralType 36 || typeof(EdmType).Equals(typeof(T)) && resultType is EdmType) 37 { 38 returnType = resultType; 39 return true; 40 } 41 } 42 returnType = null; 43 return false; 44 } 45 46 private static bool TryGetWrappedReturnEdmTypeFromFunctionImport<T>(EdmFunction functionImport, int resultSetIndex, out T resultType) where T : EdmType 47 { 48 resultType = null; 49 50 CollectionType collectionType; 51 if (TryGetFunctionImportReturnCollectionType(functionImport, resultSetIndex, out collectionType)) 52 { 53 resultType = collectionType.TypeUsage.EdmType as T; 54 return true; 55 } 56 return false; 57 } 58 59 /// <summary> 60 /// effects: determines if the given function import returns collection type, and if so returns the type 61 /// </summary> TryGetFunctionImportReturnCollectionType(EdmFunction functionImport, int resultSetIndex, out CollectionType collectionType)62 internal static bool TryGetFunctionImportReturnCollectionType(EdmFunction functionImport, int resultSetIndex, out CollectionType collectionType) 63 { 64 FunctionParameter returnParameter = GetReturnParameter(functionImport, resultSetIndex); 65 if (returnParameter != null 66 && returnParameter.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.CollectionType) 67 { 68 collectionType = (CollectionType)returnParameter.TypeUsage.EdmType; 69 return true; 70 } 71 collectionType = null; 72 return false; 73 74 } 75 76 /// <summary> 77 /// Gets the resultSetIndexth return parameter for functionImport, or null if resultSetIndex is out of range 78 /// </summary> GetReturnParameter(EdmFunction functionImport, int resultSetIndex)79 internal static FunctionParameter GetReturnParameter(EdmFunction functionImport, int resultSetIndex) 80 { 81 return functionImport.ReturnParameters.Count > resultSetIndex 82 ? functionImport.ReturnParameters[resultSetIndex] 83 : null; 84 } 85 GetFunctionImport( string functionName, string defaultContainerName, MetadataWorkspace workspace, out string containerName, out string functionImportName)86 internal static EdmFunction GetFunctionImport( 87 string functionName, string defaultContainerName, MetadataWorkspace workspace, 88 out string containerName, out string functionImportName) 89 { 90 // find FunctionImport 91 92 CommandHelper.ParseFunctionImportCommandText(functionName, defaultContainerName, 93 out containerName, out functionImportName); 94 return CommandHelper.FindFunctionImport(workspace, containerName, functionImportName); 95 } 96 97 /// <summary> 98 /// Gets the resultSetIndexth result edm type, and ensure that it is consistent with EntityType. 99 /// </summary> GetAndCheckFunctionImportReturnType(EdmFunction functionImport, int resultSetIndex, MetadataWorkspace workspace)100 internal static EdmType GetAndCheckFunctionImportReturnType<TElement>(EdmFunction functionImport, int resultSetIndex, MetadataWorkspace workspace) 101 { 102 EdmType expectedEdmType; 103 if (!MetadataHelper.TryGetFunctionImportReturnType<EdmType>(functionImport, resultSetIndex, out expectedEdmType)) 104 { 105 throw EntityUtil.ExecuteFunctionCalledWithNonReaderFunction(functionImport); 106 } 107 CheckFunctionImportReturnType<TElement>(expectedEdmType, workspace); 108 109 return expectedEdmType; 110 } 111 112 /// <summary> 113 /// check that the type TElement and function metadata are consistent 114 /// </summary> CheckFunctionImportReturnType(EdmType expectedEdmType, MetadataWorkspace workspace)115 internal static void CheckFunctionImportReturnType<TElement>(EdmType expectedEdmType, MetadataWorkspace workspace) 116 { 117 // currently there are only two possible spatial O-space types, but 16 C-space types. 118 // Normalize the C-space type to the base type before we check to see if it matches the O-space type. 119 bool isGeographic; 120 EdmType spatialNormalizedEdmType = expectedEdmType; 121 if (Helper.IsSpatialType(expectedEdmType, out isGeographic)) 122 { 123 spatialNormalizedEdmType = PrimitiveType.GetEdmPrimitiveType(isGeographic ? PrimitiveTypeKind.Geography : PrimitiveTypeKind.Geometry); 124 } 125 126 EdmType modelEdmType; 127 if (!MetadataHelper.TryDetermineCSpaceModelType<TElement>(workspace, out modelEdmType)|| 128 !modelEdmType.EdmEquals(spatialNormalizedEdmType)) 129 { 130 throw EntityUtil.ExecuteFunctionTypeMismatch(typeof(TElement), expectedEdmType); 131 } 132 } 133 134 // Returns ParameterDirection corresponding to given ParameterMode ParameterModeToParameterDirection(ParameterMode mode)135 internal static ParameterDirection ParameterModeToParameterDirection(ParameterMode mode) 136 { 137 switch (mode) 138 { 139 case ParameterMode.In: 140 return ParameterDirection.Input; 141 142 case ParameterMode.InOut: 143 return ParameterDirection.InputOutput; 144 145 case ParameterMode.Out: 146 return ParameterDirection.Output; 147 148 case ParameterMode.ReturnValue: 149 return ParameterDirection.ReturnValue; 150 151 default: 152 Debug.Fail("unrecognized mode " + mode.ToString()); 153 return default(ParameterDirection); 154 } 155 } 156 157 // requires: workspace 158 // Determines CSpace EntityType associated with the type argument T TryDetermineCSpaceModelType(MetadataWorkspace workspace, out EdmType modelEdmType)159 internal static bool TryDetermineCSpaceModelType<T>(MetadataWorkspace workspace, out EdmType modelEdmType) 160 { 161 return TryDetermineCSpaceModelType(typeof(T), workspace, out modelEdmType); 162 } 163 TryDetermineCSpaceModelType(Type type, MetadataWorkspace workspace, out EdmType modelEdmType)164 internal static bool TryDetermineCSpaceModelType(Type type, MetadataWorkspace workspace, out EdmType modelEdmType) 165 { 166 Debug.Assert(null != workspace); 167 Type nonNullabelType = TypeSystem.GetNonNullableType(type); 168 // make sure the workspace knows about T 169 workspace.ImplicitLoadAssemblyForType(nonNullabelType, System.Reflection.Assembly.GetCallingAssembly()); 170 ObjectItemCollection objectItemCollection = (ObjectItemCollection)workspace.GetItemCollection(DataSpace.OSpace); 171 EdmType objectEdmType; 172 if (objectItemCollection.TryGetItem<EdmType>(nonNullabelType.FullName, out objectEdmType)) 173 { 174 Map map; 175 if (workspace.TryGetMap(objectEdmType, DataSpace.OCSpace, out map)) 176 { 177 ObjectTypeMapping objectMapping = (ObjectTypeMapping)map; 178 modelEdmType = objectMapping.EdmType; 179 return true; 180 } 181 } 182 modelEdmType = null; 183 return false; 184 } 185 186 // effects: Returns true iff member is present in type.Members DoesMemberExist(StructuralType type, EdmMember member)187 internal static bool DoesMemberExist(StructuralType type, EdmMember member) 188 { 189 foreach (EdmMember child in type.Members) 190 { 191 if (child.Equals(member)) 192 { 193 return true; 194 } 195 } 196 return false; 197 } 198 199 /// <summary> 200 /// Returns true iff member's is a simple non-structures scalar such as primitive or enum. 201 /// </summary> IsNonRefSimpleMember(EdmMember member)202 internal static bool IsNonRefSimpleMember(EdmMember member) 203 { 204 return member.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType || 205 member.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.EnumType; 206 } 207 208 // effects: Returns true if member's type has a discrete domain (i.e. is bolean type) 209 // Note: enums don't have discrete domains as we allow domain of the underlying type. HasDiscreteDomain(EdmType edmType)210 internal static bool HasDiscreteDomain(EdmType edmType) 211 { 212 var primitiveType = edmType as PrimitiveType; 213 214 return primitiveType != null && primitiveType.PrimitiveTypeKind == PrimitiveTypeKind.Boolean; 215 } 216 217 // requires: end is given 218 // effects: determine the entity type for an association end member GetEntityTypeForEnd(AssociationEndMember end)219 internal static EntityType GetEntityTypeForEnd(AssociationEndMember end) 220 { 221 Debug.Assert(null != end); 222 Debug.Assert(end.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.RefType, 223 "type of association end member must be ref"); 224 RefType refType = (RefType)end.TypeUsage.EdmType; 225 EntityTypeBase endType = refType.ElementType; 226 Debug.Assert(endType.BuiltInTypeKind == BuiltInTypeKind.EntityType, 227 "type of association end reference element must be entity type"); 228 return (EntityType)endType; 229 } 230 231 232 // effects: Returns the entity set at the end corresponding to endMember GetEntitySetAtEnd(AssociationSet associationSet, AssociationEndMember endMember)233 internal static EntitySet GetEntitySetAtEnd(AssociationSet associationSet, 234 AssociationEndMember endMember) 235 { 236 return associationSet.AssociationSetEnds[endMember.Name].EntitySet; 237 } 238 239 // effects: Returns the AssociationEndMember at the other end of the parent association (first found) GetOtherAssociationEnd(AssociationEndMember endMember)240 internal static AssociationEndMember GetOtherAssociationEnd(AssociationEndMember endMember) 241 { 242 ReadOnlyMetadataCollection<EdmMember> members = endMember.DeclaringType.Members; 243 Debug.Assert(members.Count == 2, "only expecting two end members"); 244 245 EdmMember otherMember = members[0]; 246 if (!Object.ReferenceEquals(endMember, otherMember)) 247 { 248 Debug.Assert(Object.ReferenceEquals(endMember, members[1]), "didn't match other member"); 249 return (AssociationEndMember)otherMember; 250 } 251 return (AssociationEndMember)members[1]; 252 } 253 254 // effects: Returns true iff every end other than "endPropery" has a lower 255 // multiplicity of at least one IsEveryOtherEndAtLeastOne(AssociationSet associationSet, AssociationEndMember member)256 internal static bool IsEveryOtherEndAtLeastOne(AssociationSet associationSet, 257 AssociationEndMember member) 258 { 259 foreach (AssociationSetEnd end in associationSet.AssociationSetEnds) 260 { 261 AssociationEndMember endMember = end.CorrespondingAssociationEndMember; 262 if (endMember.Equals(member) == false && 263 GetLowerBoundOfMultiplicity(endMember.RelationshipMultiplicity) == 0) 264 { 265 return false; 266 } 267 } 268 return true; 269 } 270 271 // requires: toEnd and type are given 272 // effects: determines whether the given association end can be referenced by an entity of the given type IsAssociationValidForEntityType(AssociationSetEnd toEnd, EntityType type)273 internal static bool IsAssociationValidForEntityType(AssociationSetEnd toEnd, EntityType type) 274 { 275 Debug.Assert(null != toEnd); 276 Debug.Assert(null != type); 277 278 // get the opposite end which includes the relevant type information 279 AssociationSetEnd fromEnd = GetOppositeEnd(toEnd); 280 EntityType fromType = GetEntityTypeForEnd(fromEnd.CorrespondingAssociationEndMember); 281 return (fromType.IsAssignableFrom(type)); 282 } 283 284 // requires: end is given 285 // effects: returns the opposite end in the association GetOppositeEnd(AssociationSetEnd end)286 internal static AssociationSetEnd GetOppositeEnd(AssociationSetEnd end) 287 { 288 Debug.Assert(null != end); 289 // there must be exactly one ("Single") other end that isn't ("Filter") this end 290 AssociationSetEnd otherEnd = end.ParentAssociationSet.AssociationSetEnds.Where( 291 e => !e.EdmEquals(end)).Single(); 292 return otherEnd; 293 } 294 295 // requires: function is not null 296 // effects: Returns true if the given function is composable. IsComposable(EdmFunction function)297 internal static bool IsComposable(EdmFunction function) 298 { 299 Debug.Assert(function != null); 300 MetadataProperty isComposableProperty; 301 if (function.MetadataProperties.TryGetValue("IsComposableAttribute", false, out isComposableProperty)) 302 { 303 return (bool)isComposableProperty.Value; 304 } else 305 { 306 return !function.IsFunctionImport; 307 } 308 } 309 310 // requires: member is EdmProperty or AssociationEndMember 311 // effects: Returns true if member is nullable IsMemberNullable(EdmMember member)312 internal static bool IsMemberNullable(EdmMember member) 313 { 314 Debug.Assert(member != null); 315 Debug.Assert(Helper.IsEdmProperty(member) || Helper.IsAssociationEndMember(member)); 316 if (Helper.IsEdmProperty(member)) 317 { 318 return ((EdmProperty)member).Nullable; 319 } 320 return false; 321 } 322 323 /// <summary> 324 /// Given a table EntitySet this function finds out all C-side EntitySets that are mapped to the table. 325 /// </summary> GetInfluencingEntitySetsForTable(EntitySet table, MetadataWorkspace workspace)326 internal static IEnumerable<EntitySet> GetInfluencingEntitySetsForTable(EntitySet table, MetadataWorkspace workspace) 327 { 328 Debug.Assert(table.EntityContainer.GetDataSpace() == DataSpace.SSpace); 329 330 ItemCollection itemCollection = null; 331 workspace.TryGetItemCollection(DataSpace.CSSpace, out itemCollection); 332 StorageEntityContainerMapping containerMapping = MappingMetadataHelper.GetEntityContainerMap((StorageMappingItemCollection)itemCollection, table.EntityContainer); 333 334 //find EntitySetMappings where one of the mapping fragment maps some type to the given table 335 return containerMapping.EntitySetMaps 336 .Where( 337 map => map.TypeMappings.Any( 338 typeMap => typeMap.MappingFragments.Any( 339 mappingFrag => mappingFrag.TableSet.EdmEquals(table) 340 ) 341 ) 342 ) 343 .Select(m => m.Set) 344 .Cast<EntitySet>() 345 .Distinct(); 346 } 347 348 // effects: Returns this type and its sub types - for refs, gets the 349 // type and subtypes of the entity type GetTypeAndSubtypesOf(EdmType type, MetadataWorkspace workspace, bool includeAbstractTypes)350 internal static IEnumerable<EdmType> GetTypeAndSubtypesOf(EdmType type, MetadataWorkspace workspace, bool includeAbstractTypes) 351 { 352 return GetTypeAndSubtypesOf(type, workspace.GetItemCollection(DataSpace.CSpace), includeAbstractTypes); 353 } 354 GetTypeAndSubtypesOf(EdmType type, ItemCollection itemCollection, bool includeAbstractTypes)355 internal static IEnumerable<EdmType> GetTypeAndSubtypesOf(EdmType type, ItemCollection itemCollection, bool includeAbstractTypes) 356 { 357 // We have to collect subtypes in ref to support conditional association mappings 358 if (Helper.IsRefType(type)) 359 { 360 type = ((RefType)type).ElementType; 361 } 362 363 if (includeAbstractTypes || !type.Abstract) 364 { 365 yield return type; 366 } 367 368 // Get entity sub-types 369 foreach (EdmType subType in GetTypeAndSubtypesOf<EntityType>(type, itemCollection, includeAbstractTypes)) 370 { 371 yield return subType; 372 } 373 374 // Get complex sub-types 375 foreach (EdmType subType in GetTypeAndSubtypesOf<ComplexType>(type, itemCollection, includeAbstractTypes)) 376 { 377 yield return subType; 378 } 379 } 380 381 private static IEnumerable<EdmType> GetTypeAndSubtypesOf<T_EdmType>(EdmType type, ItemCollection itemCollection, bool includeAbstractTypes) 382 where T_EdmType : EdmType 383 { 384 // Get the subtypes of the type from the WorkSpace 385 T_EdmType specificType = type as T_EdmType; 386 if (specificType != null) 387 { 388 389 IEnumerable<T_EdmType> typesInWorkSpace = itemCollection.GetItems<T_EdmType>(); 390 foreach (T_EdmType typeInWorkSpace in typesInWorkSpace) 391 { 392 if (specificType.Equals(typeInWorkSpace) == false && Helper.IsSubtypeOf(typeInWorkSpace, specificType)) 393 { 394 if (includeAbstractTypes || !typeInWorkSpace.Abstract) 395 { 396 yield return typeInWorkSpace; 397 } 398 399 } 400 } 401 } 402 yield break; 403 } 404 405 GetTypeAndParentTypesOf(EdmType type, ItemCollection itemCollection, bool includeAbstractTypes)406 internal static IEnumerable<EdmType> GetTypeAndParentTypesOf(EdmType type, ItemCollection itemCollection, bool includeAbstractTypes) 407 { 408 // We have to collect subtypes in ref to support conditional association mappings 409 if (Helper.IsRefType(type)) 410 { 411 type = ((RefType)type).ElementType; 412 } 413 414 EdmType specificType = type; 415 while (specificType != null) 416 { 417 if (includeAbstractTypes || !specificType.Abstract) 418 { 419 yield return specificType; 420 } 421 422 specificType = specificType.BaseType as EntityType; //The cast is guaranteed to work. See use of GetItems<T_EdmType> in GetTypesAndSubTypesOf() 423 } 424 425 } 426 427 /// <summary> 428 /// Builds an undirected graph (represented as a directional graph with reciprocal navigation edges) of the all the types in the workspace. 429 /// This is used to traverse inheritance hierarchy up and down. 430 /// O(n), where n=number of types 431 /// </summary> 432 /// <returns>A dictionary of type t -> set of types {s}, such that there is an edge between t and elem(s) iff t and s are related DIRECTLY via inheritance (child or parent type) </returns> BuildUndirectedGraphOfTypes(EdmItemCollection edmItemCollection)433 internal static Dictionary<EntityType, Set<EntityType>> BuildUndirectedGraphOfTypes(EdmItemCollection edmItemCollection) 434 { 435 Dictionary<EntityType, Set<EntityType>> graph = new Dictionary<EntityType, Set<EntityType>>(); 436 437 IEnumerable<EntityType> typesInWorkSpace = edmItemCollection.GetItems<EntityType>(); 438 foreach (EntityType childType in typesInWorkSpace) 439 { 440 if (childType.BaseType == null) //root type 441 { 442 continue; 443 } 444 445 EntityType parentType = childType.BaseType as EntityType; 446 Debug.Assert(parentType != null, "Parent type not Entity Type ??"); 447 448 AddDirectedEdgeBetweenEntityTypes(graph, childType, parentType); 449 AddDirectedEdgeBetweenEntityTypes(graph, parentType, childType); 450 } 451 452 return graph; 453 } 454 455 /// <summary> 456 /// is A parent of b? 457 /// </summary> IsParentOf(EntityType a, EntityType b)458 internal static bool IsParentOf(EntityType a, EntityType b) 459 { 460 EntityType parent = b.BaseType as EntityType; 461 462 while (parent != null) 463 { 464 if (parent.EdmEquals(a)) 465 { 466 return true; 467 } 468 else 469 { 470 parent = parent.BaseType as EntityType; 471 } 472 } 473 return false; 474 } 475 476 /// <summary> 477 /// Add and Edge a --> b 478 /// Assumes edge does not exist 479 /// O(1) 480 /// </summary> AddDirectedEdgeBetweenEntityTypes(Dictionary<EntityType, Set<EntityType>> graph, EntityType a, EntityType b)481 private static void AddDirectedEdgeBetweenEntityTypes(Dictionary<EntityType, Set<EntityType>> graph, EntityType a, EntityType b) 482 { 483 Set<EntityType> references; 484 if (graph.ContainsKey(a)) 485 { 486 references = graph[a]; 487 } 488 else 489 { 490 references = new Set<EntityType>(); 491 graph.Add(a, references); 492 } 493 494 Debug.Assert(!references.Contains(b), "Dictionary already has a --> b reference"); 495 references.Add(b); 496 } 497 498 499 500 /// <summary> 501 /// Checks wither the given AssociationEnd's keys are sufficient for identifying a unique tuple in the AssociationSet. 502 /// This is possible because refconstraints make certain Keys redundant. We subtract such redundant key sof "other" ends 503 /// and see if what is left is contributed only from the given end's keys. 504 /// </summary> 505 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2140:TransparentMethodsMustNotReferenceCriticalCode", Justification = "Based on Bug VSTS Pioneer #433188: IsVisibleOutsideAssembly is wrong on generic instantiations.")] DoesEndKeySubsumeAssociationSetKey(AssociationSet assocSet, AssociationEndMember thisEnd, HashSet<Pair<EdmMember, EntityType>> associationkeys)506 internal static bool DoesEndKeySubsumeAssociationSetKey(AssociationSet assocSet, AssociationEndMember thisEnd, HashSet<Pair<EdmMember, EntityType>> associationkeys) 507 { 508 AssociationType assocType = assocSet.ElementType; 509 EntityType thisEndsEntityType = (EntityType)((RefType)thisEnd.TypeUsage.EdmType).ElementType; 510 511 HashSet<Pair<EdmMember, EntityType>> thisEndKeys = new HashSet<Pair<EdmMember, EntityType>>( 512 thisEndsEntityType.KeyMembers.Select(edmMember => new Pair<EdmMember, EntityType>(edmMember, thisEndsEntityType))); 513 514 foreach (ReferentialConstraint constraint in assocType.ReferentialConstraints) 515 { 516 IEnumerable<EdmMember> otherEndProperties; 517 EntityType otherEndType; 518 519 if (thisEnd.Equals((AssociationEndMember)constraint.ToRole)) 520 { 521 otherEndProperties = Helpers.AsSuperTypeList<EdmProperty, EdmMember>(constraint.FromProperties); 522 otherEndType = (EntityType)((RefType)((AssociationEndMember)constraint.FromRole).TypeUsage.EdmType).ElementType; 523 } 524 else if (thisEnd.Equals((AssociationEndMember)constraint.FromRole)) 525 { 526 otherEndProperties = Helpers.AsSuperTypeList<EdmProperty, EdmMember>(constraint.ToProperties); 527 otherEndType = (EntityType)((RefType)((AssociationEndMember)constraint.ToRole).TypeUsage.EdmType).ElementType; 528 } 529 else 530 { 531 //this end not part of the referential constraint 532 continue; 533 } 534 535 //Essentially ref constraints is an equality condition, so remove redundant members from entity set key 536 foreach (EdmMember member in otherEndProperties) 537 { 538 associationkeys.Remove(new Pair<EdmMember, EntityType>(member, otherEndType)); 539 } 540 } 541 542 //Now that all redundant members have been removed, is thisEnd the key of the entity set? 543 return associationkeys.IsSubsetOf(thisEndKeys); 544 } 545 546 547 // effects: Returns true if end forms a key in relationshipSet DoesEndFormKey(AssociationSet associationSet, AssociationEndMember end)548 internal static bool DoesEndFormKey(AssociationSet associationSet, AssociationEndMember end) 549 { 550 // Look at all other ends. if their multiplicities are at most 1, return true 551 foreach (AssociationEndMember endMember in associationSet.ElementType.Members) 552 { 553 if (endMember.Equals(end) == false && 554 endMember.RelationshipMultiplicity == RelationshipMultiplicity.Many) // some other end has multiplicity 0..* 555 { 556 return false; 557 } 558 } 559 return true; 560 } 561 562 // effects: Returns true if extent is at one of the ends of relationshipSet IsExtentAtSomeRelationshipEnd(AssociationSet relationshipSet, EntitySetBase extent)563 internal static bool IsExtentAtSomeRelationshipEnd(AssociationSet relationshipSet, EntitySetBase extent) 564 { 565 if (Helper.IsEntitySet(extent)) 566 { 567 return GetSomeEndForEntitySet(relationshipSet, (EntitySet)extent) != null; 568 } 569 return false; 570 } 571 572 // effects: Returns some end corresponding to entity set in 573 // association set. If no such end exists, return null GetSomeEndForEntitySet(AssociationSet associationSet, EntitySetBase entitySet)574 internal static AssociationEndMember GetSomeEndForEntitySet(AssociationSet associationSet, EntitySetBase entitySet) 575 { 576 foreach (AssociationSetEnd associationEnd in associationSet.AssociationSetEnds) 577 { 578 if (associationEnd.EntitySet.Equals(entitySet)) 579 { 580 return associationEnd.CorrespondingAssociationEndMember; 581 } 582 } 583 return null; 584 } 585 586 587 588 // requires: entitySet1 and entitySet2 belong to the same container 589 // effects: Returns the associations that occur between entitySet1 590 // and entitySet2. If none is found, returns an empty set GetAssociationsForEntitySets(EntitySet entitySet1, EntitySet entitySet2)591 internal static List<AssociationSet> GetAssociationsForEntitySets(EntitySet entitySet1, EntitySet entitySet2) 592 { 593 Debug.Assert(entitySet1 != null); 594 Debug.Assert(entitySet2 != null); 595 Debug.Assert(entitySet1.EntityContainer == entitySet2.EntityContainer, "EntityContainer must be the same for both the entity sets"); 596 597 List<AssociationSet> result = new List<AssociationSet>(); 598 599 foreach (EntitySetBase extent in entitySet1.EntityContainer.BaseEntitySets) 600 { 601 if (Helper.IsRelationshipSet(extent)) 602 { 603 AssociationSet assocSet = (AssociationSet)extent; 604 if (IsExtentAtSomeRelationshipEnd(assocSet, entitySet1) && 605 IsExtentAtSomeRelationshipEnd(assocSet, entitySet2)) 606 { 607 result.Add(assocSet); 608 } 609 } 610 } 611 return result; 612 } 613 614 // requires: entitySet and associationType 615 // effects: Returns the associations that refer to associationType and refer to entitySet in one of its end. 616 // If none is found, returns an empty set GetAssociationsForEntitySetAndAssociationType(EntityContainer entityContainer, string entitySetName, AssociationType associationType, string endName, out EntitySet entitySet)617 internal static AssociationSet GetAssociationsForEntitySetAndAssociationType(EntityContainer entityContainer, string entitySetName, 618 AssociationType associationType, string endName, out EntitySet entitySet) 619 { 620 Debug.Assert(associationType.Members.Contains(endName), "EndName should be a valid name"); 621 entitySet = null; 622 AssociationSet retValue = null; 623 ReadOnlyMetadataCollection<EntitySetBase> baseEntitySets = entityContainer.BaseEntitySets; 624 int count = baseEntitySets.Count; 625 for (int i = 0; i < count; ++i) 626 { 627 EntitySetBase extent = baseEntitySets[i]; 628 if (Object.ReferenceEquals(extent.ElementType, associationType)) 629 { 630 AssociationSet assocSet = (AssociationSet)extent; 631 EntitySet es = assocSet.AssociationSetEnds[endName].EntitySet; 632 if (es.Name == entitySetName) 633 { 634 Debug.Assert(retValue == null, "There should be only one AssociationSet, given an assocationtype, end name and entity set"); 635 retValue = assocSet; 636 entitySet = es; 637 #if !DEBUG 638 break; 639 #endif 640 } 641 } 642 } 643 return retValue; 644 } 645 646 // requires: entitySet 647 // effects: Returns the associations that occur between entitySet 648 // and other entitySets. If none is found, returns an empty set GetAssociationsForEntitySet(EntitySetBase entitySet)649 internal static List<AssociationSet> GetAssociationsForEntitySet(EntitySetBase entitySet) 650 { 651 Debug.Assert(entitySet != null); 652 653 List<AssociationSet> result = new List<AssociationSet>(); 654 655 foreach (EntitySetBase extent in entitySet.EntityContainer.BaseEntitySets) 656 { 657 if (Helper.IsRelationshipSet(extent)) 658 { 659 AssociationSet assocSet = (AssociationSet)extent; 660 if (IsExtentAtSomeRelationshipEnd(assocSet, entitySet)) 661 { 662 result.Add(assocSet); 663 } 664 } 665 } 666 return result; 667 } 668 669 // effects: Returns true iff superType is an ancestor of subType in 670 // the type hierarchy or superType and subType are the same IsSuperTypeOf(EdmType superType, EdmType subType)671 internal static bool IsSuperTypeOf(EdmType superType, EdmType subType) 672 { 673 EdmType currentType = subType; 674 while (currentType != null) 675 { 676 if (currentType.Equals(superType)) 677 { 678 return true; 679 } 680 currentType = currentType.BaseType; 681 } 682 return false; 683 } 684 685 // requires: typeUsage wraps a primitive type GetPrimitiveTypeKind(TypeUsage typeUsage)686 internal static PrimitiveTypeKind GetPrimitiveTypeKind(TypeUsage typeUsage) 687 { 688 Debug.Assert(null != typeUsage && null != typeUsage.EdmType && typeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType); 689 690 PrimitiveType primitiveType = (PrimitiveType)typeUsage.EdmType; 691 692 return primitiveType.PrimitiveTypeKind; 693 } 694 695 // determines whether the given member is a key of an entity set IsPartOfEntityTypeKey(EdmMember member)696 internal static bool IsPartOfEntityTypeKey(EdmMember member) 697 { 698 if (Helper.IsEntityType(member.DeclaringType) && 699 Helper.IsEdmProperty(member)) 700 { 701 return ((EntityType)member.DeclaringType).KeyMembers.Contains(member); 702 } 703 704 return false; 705 } 706 707 // Given a type usage, returns the element type (unwraps collections) GetElementType(TypeUsage typeUsage)708 internal static TypeUsage GetElementType(TypeUsage typeUsage) 709 { 710 if (BuiltInTypeKind.CollectionType == typeUsage.EdmType.BuiltInTypeKind) 711 { 712 TypeUsage elementType = ((CollectionType)typeUsage.EdmType).TypeUsage; 713 // recursively unwrap 714 return GetElementType(elementType); 715 } 716 return typeUsage; 717 } 718 GetLowerBoundOfMultiplicity(RelationshipMultiplicity multiplicity)719 internal static int GetLowerBoundOfMultiplicity(RelationshipMultiplicity multiplicity) 720 { 721 if (multiplicity == RelationshipMultiplicity.Many || 722 multiplicity == RelationshipMultiplicity.ZeroOrOne) 723 { 724 return 0; 725 } 726 else 727 { 728 return 1; 729 } 730 } 731 GetUpperBoundOfMultiplicity(RelationshipMultiplicity multiplicity)732 internal static int? GetUpperBoundOfMultiplicity(RelationshipMultiplicity multiplicity) 733 { 734 if (multiplicity == RelationshipMultiplicity.One || 735 multiplicity == RelationshipMultiplicity.ZeroOrOne) 736 { 737 return 1; 738 } 739 else 740 { 741 return null; 742 } 743 } 744 745 // effects: Returns all the concurrency token members in superType and its subtypes GetConcurrencyMembersForTypeHierarchy(EntityTypeBase superType, EdmItemCollection edmItemCollection)746 internal static Set<EdmMember> GetConcurrencyMembersForTypeHierarchy(EntityTypeBase superType, EdmItemCollection edmItemCollection) 747 { 748 Set<EdmMember> result = new Set<EdmMember>(); 749 foreach (StructuralType type in GetTypeAndSubtypesOf(superType, edmItemCollection, true /*includeAbstractTypes */ )) 750 { 751 752 // Go through all the members -- Can call Members instead of AllMembers since we are 753 // running through the whole hierarchy 754 foreach (EdmMember member in type.Members) 755 { 756 // check for the concurrency facet 757 ConcurrencyMode concurrencyMode = GetConcurrencyMode(member); 758 if (concurrencyMode == ConcurrencyMode.Fixed) 759 { 760 result.Add(member); 761 } 762 } 763 } 764 return result; 765 } 766 767 // Determines whether the given member is declared as a concurrency property GetConcurrencyMode(EdmMember member)768 internal static ConcurrencyMode GetConcurrencyMode(EdmMember member) 769 { 770 return GetConcurrencyMode(member.TypeUsage); 771 } 772 773 // Determines whether the given member is declared as a concurrency property GetConcurrencyMode(TypeUsage typeUsage)774 internal static ConcurrencyMode GetConcurrencyMode(TypeUsage typeUsage) 775 { 776 Facet concurrencyFacet; 777 if (typeUsage.Facets.TryGetValue(EdmProviderManifest.ConcurrencyModeFacetName, false, out concurrencyFacet) && 778 concurrencyFacet.Value != null) 779 { 780 ConcurrencyMode concurrencyMode = (ConcurrencyMode)concurrencyFacet.Value; 781 return concurrencyMode; 782 } 783 return ConcurrencyMode.None; 784 } 785 786 // Determines the store generated pattern for this member GetStoreGeneratedPattern(EdmMember member)787 internal static StoreGeneratedPattern GetStoreGeneratedPattern(EdmMember member) 788 { 789 Facet storeGeneratedFacet; 790 if (member.TypeUsage.Facets.TryGetValue(EdmProviderManifest.StoreGeneratedPatternFacetName, false, out storeGeneratedFacet) && 791 storeGeneratedFacet.Value != null) 792 { 793 StoreGeneratedPattern pattern = (StoreGeneratedPattern)storeGeneratedFacet.Value; 794 return pattern; 795 } 796 return StoreGeneratedPattern.None; 797 } 798 799 /// <summary> 800 /// Check if all the SchemaErrors have the serverity of SchemaErrorSeverity.Warning 801 /// </summary> 802 /// <param name="schemaErrors"></param> 803 /// <returns></returns> CheckIfAllErrorsAreWarnings(IList<EdmSchemaError> schemaErrors)804 internal static bool CheckIfAllErrorsAreWarnings(IList<EdmSchemaError> schemaErrors) 805 { 806 int length = schemaErrors.Count; 807 for (int i = 0; i < length; ++i) 808 { 809 EdmSchemaError error = schemaErrors[i]; 810 if (error.Severity != EdmSchemaErrorSeverity.Warning) 811 { 812 return false; 813 } 814 } 815 return true; 816 } 817 818 /// <summary> 819 /// 820 /// </summary> 821 /// <param name="dictionaryExtentViews"></param> 822 /// <returns></returns> GenerateHashForAllExtentViewsContent(double schemaVersion, IEnumerable<KeyValuePair<string, string>> extentViews)823 internal static string GenerateHashForAllExtentViewsContent(double schemaVersion, IEnumerable<KeyValuePair<string, string>> extentViews) 824 { 825 CompressingHashBuilder builder = new CompressingHashBuilder(CreateMetadataHashAlgorithm(schemaVersion)); 826 foreach (var view in extentViews) 827 { 828 builder.AppendLine(view.Key); 829 builder.AppendLine(view.Value); 830 } 831 return builder.ComputeHash(); 832 } 833 834 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Cryptographic.Standard", "CA5350:Microsoft.Cryptographic.Standard", 835 Justification = "MD5CryptoServiceProvider is not used for cryptography/security purposes and we do it only for v1 and v1.1 for compatibility reasons.")] 836 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security.Cryptography", "CA5350:Microsoft.Cryptographic.Standard", 837 Justification = "MD5CryptoServiceProvider is not used for cryptography/security purposes and we do it only for v1 and v1.1 for compatibility reasons.")] CreateMetadataHashAlgorithm(double schemaVersion)838 internal static HashAlgorithm CreateMetadataHashAlgorithm(double schemaVersion) 839 { 840 HashAlgorithm hashAlgorithm; 841 if (schemaVersion < XmlConstants.EdmVersionForV2) 842 { 843 // v1 and v1.1 use old hash to remain compatible 844 hashAlgorithm = new MD5CryptoServiceProvider(); 845 } 846 else 847 { 848 // v2 and above use a FIPS approved provider 849 // so that when FIPS only is enforced by the OS 850 // we still work 851 hashAlgorithm = CreateSHA256HashAlgorithm(); 852 } 853 return hashAlgorithm; 854 } 855 CreateSHA256HashAlgorithm()856 internal static SHA256 CreateSHA256HashAlgorithm() 857 { 858 SHA256 sha256HashAlgorith; 859 try 860 { 861 // use the FIPS compliant SHA256 implementation 862 sha256HashAlgorith = new SHA256CryptoServiceProvider(); 863 } 864 catch (PlatformNotSupportedException) 865 { 866 // the FIPS compliant (and faster) algorith was not available, create the managed version 867 // this will throw if FIPS only is enforced 868 sha256HashAlgorith = new SHA256Managed(); 869 } 870 871 return sha256HashAlgorith; 872 } 873 ConvertStoreTypeUsageToEdmTypeUsage(TypeUsage storeTypeUsage)874 internal static TypeUsage ConvertStoreTypeUsageToEdmTypeUsage(TypeUsage storeTypeUsage) 875 { 876 TypeUsage edmTypeUsage = storeTypeUsage.GetModelTypeUsage().ShallowCopy(FacetValues.NullFacetValues); 877 878 // we don't reason the facets during the function resolution any more 879 880 return edmTypeUsage; 881 882 } 883 GetPrecision(this TypeUsage type)884 internal static byte GetPrecision(this TypeUsage type) 885 { 886 return type.GetFacetValue<byte>("Precision"); 887 } 888 GetScale(this TypeUsage type)889 internal static byte GetScale(this TypeUsage type) 890 { 891 return type.GetFacetValue<byte>("Scale"); 892 } 893 GetMaxLength(this TypeUsage type)894 internal static int GetMaxLength(this TypeUsage type) 895 { 896 return type.GetFacetValue<int>("MaxLength"); 897 } 898 GetFacetValue(this TypeUsage type, string facetName)899 internal static T GetFacetValue<T>(this TypeUsage type, string facetName) 900 { 901 return (T)type.Facets[facetName].Value; 902 } 903 #region NavigationPropertyAccessor Helpers 904 GetNavigationPropertyAccessor(EntityType sourceEntityType, AssociationEndMember sourceMember, AssociationEndMember targetMember)905 internal static NavigationPropertyAccessor GetNavigationPropertyAccessor(EntityType sourceEntityType, AssociationEndMember sourceMember, AssociationEndMember targetMember) 906 { 907 Debug.Assert(sourceEntityType.DataSpace == DataSpace.OSpace && sourceEntityType.ClrType != null, "sourceEntityType must contain an ospace type"); 908 return GetNavigationPropertyAccessor(sourceEntityType, sourceMember.DeclaringType.FullName, sourceMember.Name, targetMember.Name); 909 } 910 GetNavigationPropertyAccessor(EntityType entityType, string relationshipType, string fromName, string toName)911 internal static NavigationPropertyAccessor GetNavigationPropertyAccessor(EntityType entityType, string relationshipType, string fromName, string toName) 912 { 913 NavigationProperty navigationProperty; 914 if (entityType.TryGetNavigationProperty(relationshipType, fromName, toName, out navigationProperty)) 915 { 916 return navigationProperty.Accessor; 917 } 918 else 919 { 920 return NavigationPropertyAccessor.NoNavigationProperty; 921 } 922 } 923 924 #endregion 925 926 } 927 } 928