6015d34a16ac9b13d1b2d116ef43a379af46ae28
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Common / Utils / MetadataHelper.cs
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>
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>
79         internal static FunctionParameter GetReturnParameter(EdmFunction functionImport, int resultSetIndex)
80         {
81             return functionImport.ReturnParameters.Count > resultSetIndex
82                 ? functionImport.ReturnParameters[resultSetIndex]
83                 : null;
84         }
85
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>
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>
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
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
159         internal static bool TryDetermineCSpaceModelType<T>(MetadataWorkspace workspace, out EdmType modelEdmType)
160         {
161             return TryDetermineCSpaceModelType(typeof(T), workspace, out modelEdmType);
162         }
163
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
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>
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.
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
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
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)
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
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
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
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.
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
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>
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
350         internal static IEnumerable<EdmType> GetTypeAndSubtypesOf(EdmType type, MetadataWorkspace workspace, bool includeAbstractTypes)
351         {
352             return GetTypeAndSubtypesOf(type, workspace.GetItemCollection(DataSpace.CSpace), includeAbstractTypes);
353         }
354
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
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>
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>
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>
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.")]
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
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
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
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
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
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
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
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
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
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) 
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
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
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
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
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
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
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>
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>
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         internal static HashAlgorithm CreateMetadataHashAlgorithm(double schemaVersion)
837         {
838             HashAlgorithm hashAlgorithm;
839             if (schemaVersion < XmlConstants.EdmVersionForV2)
840             {
841                 // v1 and v1.1 use old hash to remain compatible
842                 hashAlgorithm = new MD5CryptoServiceProvider();
843             }
844             else
845             {
846                 // v2 and above use a FIPS approved provider 
847                 // so that when FIPS only is enforced by the OS 
848                 // we still work
849                 hashAlgorithm = CreateSHA256HashAlgorithm();
850             }
851             return hashAlgorithm;
852         }
853
854         internal static SHA256 CreateSHA256HashAlgorithm()
855         {
856             SHA256 sha256HashAlgorith;
857             try
858             {
859                 // use the FIPS compliant SHA256 implementation
860                 sha256HashAlgorith = new SHA256CryptoServiceProvider();
861             }
862             catch (PlatformNotSupportedException)
863             {
864                 // the FIPS compliant (and faster) algorith was not available, create the managed version
865                 // this will throw if FIPS only is enforced
866                 sha256HashAlgorith = new SHA256Managed();
867             }
868
869             return sha256HashAlgorith;
870         }
871
872         internal static TypeUsage ConvertStoreTypeUsageToEdmTypeUsage(TypeUsage storeTypeUsage)
873         {
874             TypeUsage edmTypeUsage = storeTypeUsage.GetModelTypeUsage().ShallowCopy(FacetValues.NullFacetValues);
875
876             // we don't reason the facets during the function resolution any more
877
878             return edmTypeUsage;
879
880         }
881
882         internal static byte GetPrecision(this TypeUsage type)
883         {
884             return type.GetFacetValue<byte>("Precision");
885         }
886
887         internal static byte GetScale(this TypeUsage type)
888         {
889             return type.GetFacetValue<byte>("Scale");
890         }
891
892         internal static int GetMaxLength(this TypeUsage type)
893         {
894             return type.GetFacetValue<int>("MaxLength");
895         }
896
897         internal static T GetFacetValue<T>(this TypeUsage type, string facetName)
898         {
899             return (T)type.Facets[facetName].Value;
900         }
901         #region NavigationPropertyAccessor Helpers
902
903         internal static NavigationPropertyAccessor GetNavigationPropertyAccessor(EntityType sourceEntityType, AssociationEndMember sourceMember, AssociationEndMember targetMember)
904         {
905             Debug.Assert(sourceEntityType.DataSpace == DataSpace.OSpace && sourceEntityType.ClrType != null, "sourceEntityType must contain an ospace type");
906             return GetNavigationPropertyAccessor(sourceEntityType, sourceMember.DeclaringType.FullName, sourceMember.Name, targetMember.Name);
907         }
908
909         internal static NavigationPropertyAccessor GetNavigationPropertyAccessor(EntityType entityType, string relationshipType, string fromName, string toName)
910         {
911             NavigationProperty navigationProperty;
912             if (entityType.TryGetNavigationProperty(relationshipType, fromName, toName, out navigationProperty))
913             {
914                 return navigationProperty.Accessor;
915             }
916             else
917             {
918                 return NavigationPropertyAccessor.NoNavigationProperty;
919             }
920         }
921
922         #endregion
923
924     }
925 }