Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Mapping / Update / Internal / AssociationSetMetadata.cs
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>
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>
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         
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
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 }