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