1 //--------------------------------------------------------------------- 2 // <copyright file="AssociationSetMetadata.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // 6 // @owner Microsoft 7 // @backupOwner Microsoft 8 //--------------------------------------------------------------------- 9 10 using System.Data.Metadata.Edm; 11 using System.Data.Common.Utils; 12 using System.Data.Common.CommandTrees; 13 using System.Collections.Generic; 14 using System.Linq; 15 namespace System.Data.Mapping.Update.Internal 16 { 17 /// <summary> 18 /// Encapsulates information about ends of an association set needed to correctly 19 /// interpret updates. 20 /// </summary> 21 internal sealed class AssociationSetMetadata 22 { 23 /// <summary> 24 /// Gets association ends that must be modified if the association 25 /// is changed (e.g. the mapping of the association is conditioned 26 /// on some property of the end) 27 /// </summary> 28 internal readonly Set<AssociationEndMember> RequiredEnds; 29 /// <summary> 30 /// Gets association ends that may be implicitly modified as a result 31 /// of changes to the association (e.g. collocated entity with server 32 /// generated value) 33 /// </summary> 34 internal readonly Set<AssociationEndMember> OptionalEnds; 35 /// <summary> 36 /// Gets association ends whose values may influence the association 37 /// (e.g. where there is a ReferentialIntegrity or "foreign key" constraint) 38 /// </summary> 39 internal readonly Set<AssociationEndMember> IncludedValueEnds; 40 /// <summary> 41 /// true iff. there are interesting ends for this association set. 42 /// </summary> 43 internal bool HasEnds 44 { 45 get { return 0 < RequiredEnds.Count || 0 < OptionalEnds.Count || 0 < IncludedValueEnds.Count; } 46 } 47 48 /// <summary> 49 /// Initialize Metadata for an AssociationSet 50 /// </summary> AssociationSetMetadata(Set<EntitySet> affectedTables, AssociationSet associationSet, MetadataWorkspace workspace)51 internal AssociationSetMetadata(Set<EntitySet> affectedTables, AssociationSet associationSet, MetadataWorkspace workspace) 52 { 53 // If there is only 1 table, there can be no ambiguity about the "destination" of a relationship, so such 54 // sets are not typically required. 55 bool isRequired = 1 < affectedTables.Count; 56 57 // determine the ends of the relationship 58 var ends = associationSet.AssociationSetEnds; 59 60 // find collocated entities 61 foreach (EntitySet table in affectedTables) 62 { 63 // Find extents influencing the table 64 var influencingExtents = MetadataHelper.GetInfluencingEntitySetsForTable(table, workspace); 65 66 foreach (EntitySet influencingExtent in influencingExtents) 67 { 68 foreach (var end in ends) 69 { 70 // If the extent is an end of the relationship and we haven't already added it to the 71 // required set... 72 if (end.EntitySet.EdmEquals(influencingExtent)) 73 { 74 if (isRequired) 75 { 76 AddEnd(ref RequiredEnds, end.CorrespondingAssociationEndMember); 77 } 78 else if (null == RequiredEnds || !RequiredEnds.Contains(end.CorrespondingAssociationEndMember)) 79 { 80 AddEnd(ref OptionalEnds, end.CorrespondingAssociationEndMember); 81 } 82 } 83 } 84 } 85 } 86 87 // fix Required and Optional sets 88 FixSet(ref RequiredEnds); 89 FixSet(ref OptionalEnds); 90 91 // for associations with referential constraints, the principal end is always interesting 92 // since its key values may take precedence over the key values of the dependent end 93 foreach (ReferentialConstraint constraint in associationSet.ElementType.ReferentialConstraints) 94 { 95 // FromRole is the principal end in the referential constraint 96 AssociationEndMember principalEnd = (AssociationEndMember)constraint.FromRole; 97 98 if (!RequiredEnds.Contains(principalEnd) && 99 !OptionalEnds.Contains(principalEnd)) 100 { 101 AddEnd(ref IncludedValueEnds, principalEnd); 102 } 103 } 104 105 FixSet(ref IncludedValueEnds); 106 } 107 108 /// <summary> 109 /// Initialize given required ends. 110 /// </summary> AssociationSetMetadata(IEnumerable<AssociationEndMember> requiredEnds)111 internal AssociationSetMetadata(IEnumerable<AssociationEndMember> requiredEnds) 112 { 113 if (requiredEnds.Any()) 114 { 115 RequiredEnds = new Set<AssociationEndMember>(requiredEnds); 116 } 117 FixSet(ref RequiredEnds); 118 FixSet(ref OptionalEnds); 119 FixSet(ref IncludedValueEnds); 120 } 121 AddEnd(ref Set<AssociationEndMember> set, AssociationEndMember element)122 static private void AddEnd(ref Set<AssociationEndMember> set, AssociationEndMember element) 123 { 124 if (null == set) 125 { 126 set = new Set<AssociationEndMember>(); 127 } 128 set.Add(element); 129 } 130 FixSet(ref Set<AssociationEndMember> set)131 static private void FixSet(ref Set<AssociationEndMember> set) 132 { 133 if (null == set) 134 { 135 set = Set<AssociationEndMember>.Empty; 136 } 137 else 138 { 139 set.MakeReadOnly(); 140 } 141 } 142 } 143 } 144