1 //---------------------------------------------------------------------
2 // <copyright file="ReferentialConstraint.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 namespace System.Data.EntityModel.SchemaObjectModel
12 using System.Data.Metadata.Edm;
13 using System.Diagnostics;
17 /// Represents an referential constraint on a relationship
19 internal sealed class ReferentialConstraint : SchemaElement
21 private const char KEY_DELIMITER = ' ';
22 private ReferentialConstraintRoleElement _principalRole;
23 private ReferentialConstraintRoleElement _dependentRole;
26 /// construct a Referential constraint
28 /// <param name="relationship"></param>
29 public ReferentialConstraint(Relationship relationship)
35 /// Validate this referential constraint
37 internal override void Validate()
40 _principalRole.Validate();
41 _dependentRole.Validate();
43 if (ReadyForFurtherValidation(_principalRole) && ReadyForFurtherValidation(_dependentRole))
45 // Validate the to end and from end of the referential constraint
46 IRelationshipEnd principalRoleEnd = _principalRole.End;
47 IRelationshipEnd dependentRoleEnd = _dependentRole.End;
49 bool isPrinicipalRoleKeyProperty, isDependentRoleKeyProperty;
50 bool areAllPrinicipalRolePropertiesNullable, areAllDependentRolePropertiesNullable;
51 bool isDependentRolePropertiesSubsetofKeyProperties, isPrinicipalRolePropertiesSubsetofKeyProperties;
52 bool isAnyPrinicipalRolePropertyNullable, isAnyDependentRolePropertyNullable;
54 // Validate the role name to be different
55 if (_principalRole.Name == _dependentRole.Name)
57 AddError(ErrorCode.SameRoleReferredInReferentialConstraint,
58 EdmSchemaErrorSeverity.Error,
59 System.Data.Entity.Strings.SameRoleReferredInReferentialConstraint(this.ParentElement.Name));
62 // Resolve all the property in the ToProperty attribute. Also checks whether this is nullable or not and
63 // whether the properties are the keys for the type in the ToRole
64 IsKeyProperty(_dependentRole, dependentRoleEnd.Type,
65 out isPrinicipalRoleKeyProperty,
66 out areAllDependentRolePropertiesNullable,
67 out isAnyDependentRolePropertyNullable,
68 out isDependentRolePropertiesSubsetofKeyProperties);
70 // Resolve all the property in the ToProperty attribute. Also checks whether this is nullable or not and
71 // whether the properties are the keys for the type in the ToRole
72 IsKeyProperty(_principalRole, principalRoleEnd.Type,
73 out isDependentRoleKeyProperty,
74 out areAllPrinicipalRolePropertiesNullable,
75 out isAnyPrinicipalRolePropertyNullable,
76 out isPrinicipalRolePropertiesSubsetofKeyProperties);
78 Debug.Assert(_principalRole.RoleProperties.Count != 0, "There should be some ref properties in Principal Role");
79 Debug.Assert(_dependentRole.RoleProperties.Count != 0, "There should be some ref properties in Dependent Role");
81 // The properties in the PrincipalRole must be the key of the Entity type referred to by the principal role
82 if (!isDependentRoleKeyProperty)
84 AddError(ErrorCode.InvalidPropertyInRelationshipConstraint,
85 EdmSchemaErrorSeverity.Error,
86 System.Data.Entity.Strings.InvalidFromPropertyInRelationshipConstraint(
87 PrincipalRole.Name, principalRoleEnd.Type.FQName, this.ParentElement.FQName));
91 bool v1Behavior = Schema.SchemaVersion <= XmlConstants.EdmVersionForV1_1;
93 // Determine expected multiplicities
94 RelationshipMultiplicity expectedPrincipalMultiplicity = (v1Behavior
95 ? areAllPrinicipalRolePropertiesNullable
96 : isAnyPrinicipalRolePropertyNullable)
97 ? RelationshipMultiplicity.ZeroOrOne
98 : RelationshipMultiplicity.One;
99 RelationshipMultiplicity expectedDependentMultiplicity = (v1Behavior
100 ? areAllDependentRolePropertiesNullable
101 : isAnyDependentRolePropertyNullable)
102 ? RelationshipMultiplicity.ZeroOrOne
103 : RelationshipMultiplicity.Many;
104 principalRoleEnd.Multiplicity = principalRoleEnd.Multiplicity ?? expectedPrincipalMultiplicity;
105 dependentRoleEnd.Multiplicity = dependentRoleEnd.Multiplicity ?? expectedDependentMultiplicity;
107 // Since the FromProperty must be the key of the FromRole, the FromRole cannot be '*' as multiplicity
108 // Also the lower bound of multiplicity of FromRole can be zero if and only if all the properties in
109 // ToProperties are nullable
111 if (principalRoleEnd.Multiplicity == RelationshipMultiplicity.Many)
113 AddError(ErrorCode.InvalidMultiplicityInRoleInRelationshipConstraint,
114 EdmSchemaErrorSeverity.Error,
115 System.Data.Entity.Strings.InvalidMultiplicityFromRoleUpperBoundMustBeOne(_principalRole.Name, this.ParentElement.Name));
117 else if (areAllDependentRolePropertiesNullable
118 && principalRoleEnd.Multiplicity == RelationshipMultiplicity.One)
120 string message = System.Data.Entity.Strings.InvalidMultiplicityFromRoleToPropertyNullableV1(_principalRole.Name, this.ParentElement.Name);
121 AddError(ErrorCode.InvalidMultiplicityInRoleInRelationshipConstraint,
122 EdmSchemaErrorSeverity.Error,
126 (v1Behavior && !areAllDependentRolePropertiesNullable) ||
127 (!v1Behavior && !isAnyDependentRolePropertyNullable)
129 && principalRoleEnd.Multiplicity != RelationshipMultiplicity.One)
134 message = System.Data.Entity.Strings.InvalidMultiplicityFromRoleToPropertyNonNullableV1(_principalRole.Name, this.ParentElement.Name);
138 message = System.Data.Entity.Strings.InvalidMultiplicityFromRoleToPropertyNonNullableV2(_principalRole.Name, this.ParentElement.Name);
140 AddError(ErrorCode.InvalidMultiplicityInRoleInRelationshipConstraint,
141 EdmSchemaErrorSeverity.Error,
145 // If the ToProperties form the key of the type in ToRole, then the upper bound of the multiplicity
146 // of the ToRole must be '1'. The lower bound must always be zero since there can be entries in the from
147 // column which are not related to child columns.
148 if (dependentRoleEnd.Multiplicity == RelationshipMultiplicity.One && Schema.DataModel == SchemaDataModelOption.ProviderDataModel)
150 AddError(ErrorCode.InvalidMultiplicityInRoleInRelationshipConstraint,
151 EdmSchemaErrorSeverity.Error,
152 System.Data.Entity.Strings.InvalidMultiplicityToRoleLowerBoundMustBeZero(_dependentRole.Name, this.ParentElement.Name));
155 // Need to constrain the dependent role in CSDL to Key properties if this is not a IsForeignKey
157 if ((!isDependentRolePropertiesSubsetofKeyProperties) &&
158 (!this.ParentElement.IsForeignKey) &&
159 (Schema.DataModel == SchemaDataModelOption.EntityDataModel))
161 AddError(ErrorCode.InvalidPropertyInRelationshipConstraint,
162 EdmSchemaErrorSeverity.Error,
163 System.Data.Entity.Strings.InvalidToPropertyInRelationshipConstraint(
164 DependentRole.Name, dependentRoleEnd.Type.FQName, this.ParentElement.FQName));
168 // If the ToProperty is a key property, then the upper bound must be 1 i.e. every parent (from property) can
169 // have exactly one child
170 if (isPrinicipalRoleKeyProperty)
172 if (dependentRoleEnd.Multiplicity == RelationshipMultiplicity.Many)
174 AddError(ErrorCode.InvalidMultiplicityInRoleInRelationshipConstraint,
175 EdmSchemaErrorSeverity.Error,
176 System.Data.Entity.Strings.InvalidMultiplicityToRoleUpperBoundMustBeOne(dependentRoleEnd.Name, this.ParentElement.Name));
179 // if the ToProperty is not the key, then the upper bound must be many i.e every parent (from property) can
180 // be related to many childs
181 else if (dependentRoleEnd.Multiplicity != RelationshipMultiplicity.Many)
183 AddError(ErrorCode.InvalidMultiplicityInRoleInRelationshipConstraint,
184 EdmSchemaErrorSeverity.Error,
185 System.Data.Entity.Strings.InvalidMultiplicityToRoleUpperBoundMustBeMany(dependentRoleEnd.Name, this.ParentElement.Name));
188 if (_dependentRole.RoleProperties.Count != _principalRole.RoleProperties.Count)
190 AddError(ErrorCode.MismatchNumberOfPropertiesInRelationshipConstraint,
191 EdmSchemaErrorSeverity.Error,
192 System.Data.Entity.Strings.MismatchNumberOfPropertiesinRelationshipConstraint);
196 for (int i = 0; i < _dependentRole.RoleProperties.Count; i++)
198 if (_dependentRole.RoleProperties[i].Property.Type != _principalRole.RoleProperties[i].Property.Type)
200 AddError(ErrorCode.TypeMismatchRelationshipConstaint,
201 EdmSchemaErrorSeverity.Error,
202 System.Data.Entity.Strings.TypeMismatchRelationshipConstaint(
203 _dependentRole.RoleProperties[i].Name,
204 _dependentRole.End.Type.Identity,
205 _principalRole.RoleProperties[i].Name,
206 _principalRole.End.Type.Identity,
207 this.ParentElement.Name
216 private static bool ReadyForFurtherValidation(ReferentialConstraintRoleElement role)
224 if(role.RoleProperties.Count == 0)
227 foreach(PropertyRefElement propRef in role.RoleProperties)
229 if(propRef.Property == null)
237 /// Resolves the given property names to the property in the item
238 /// Also checks whether the properties form the key for the given type and whether all the properties are nullable or not
240 /// <param name="roleElement"></param>
241 /// <param name="itemType"></param>
242 /// <param name="isKeyProperty"></param>
243 /// <param name="areAllPropertiesNullable"></param>
244 /// <param name="isSubsetOfKeyProperties"></param>
245 private static void IsKeyProperty(ReferentialConstraintRoleElement roleElement, SchemaEntityType itemType,
246 out bool isKeyProperty,
247 out bool areAllPropertiesNullable,
248 out bool isAnyPropertyNullable,
249 out bool isSubsetOfKeyProperties)
251 isKeyProperty = true;
252 areAllPropertiesNullable = true;
253 isAnyPropertyNullable = false;
254 isSubsetOfKeyProperties = true;
256 if (itemType.KeyProperties.Count != roleElement.RoleProperties.Count)
258 isKeyProperty = false;
261 // Checking that ToProperties must be the key properties in the entity type referred by the ToRole
262 for (int i = 0; i < roleElement.RoleProperties.Count; i++)
264 // Once we find that the properties in the constraint are not a subset of the
265 // Key, one need not search for it every time
266 if (isSubsetOfKeyProperties)
269 bool foundKeyProperty = false;
271 // All properties that are defined in ToProperties must be the key property on the entity type
272 for (int j = 0; j < itemType.KeyProperties.Count; j++)
274 if (itemType.KeyProperties[j].Property == roleElement.RoleProperties[i].Property)
276 foundKeyProperty = true;
281 if (!foundKeyProperty)
283 isKeyProperty = false;
284 isSubsetOfKeyProperties = false;
288 areAllPropertiesNullable &= roleElement.RoleProperties[i].Property.Nullable;
289 isAnyPropertyNullable |= roleElement.RoleProperties[i].Property.Nullable;
293 protected override bool HandleAttribute(XmlReader reader)
298 protected override bool HandleElement(XmlReader reader)
300 if (base.HandleElement(reader))
304 else if (CanHandleElement(reader, XmlConstants.PrincipalRole))
306 HandleReferentialConstraintPrincipalRoleElement(reader);
309 else if (CanHandleElement(reader, XmlConstants.DependentRole))
311 HandleReferentialConstraintDependentRoleElement(reader);
318 internal void HandleReferentialConstraintPrincipalRoleElement(XmlReader reader)
320 _principalRole = new ReferentialConstraintRoleElement(this);
321 _principalRole.Parse(reader);
324 internal void HandleReferentialConstraintDependentRoleElement(XmlReader reader)
326 _dependentRole = new ReferentialConstraintRoleElement(this);
327 _dependentRole.Parse(reader);
330 internal override void ResolveTopLevelNames()
332 _dependentRole.ResolveTopLevelNames();
334 _principalRole.ResolveTopLevelNames();
338 /// The parent element as an IRelationship
340 internal new IRelationship ParentElement
344 return (IRelationship)(base.ParentElement);
348 internal ReferentialConstraintRoleElement PrincipalRole
352 return _principalRole;
356 internal ReferentialConstraintRoleElement DependentRole
360 return _dependentRole;