/// <summary> /// Add the rel property induced by the specified relationship, (if the target /// end has a multiplicity of one) /// We only keep track of rel-properties that are "interesting" /// </summary> /// <param name="associationType">the association relationship</param> /// <param name="fromEnd">source end of the relationship traversal</param> /// <param name="toEnd">target end of the traversal</param> private void AddRelProperty( AssociationType associationType, AssociationEndMember fromEnd, AssociationEndMember toEnd) { if (toEnd.RelationshipMultiplicity == RelationshipMultiplicity.Many) { return; } var prop = new RelProperty(associationType, fromEnd, toEnd); if (_interestingRelProperties == null || !_interestingRelProperties.Contains(prop)) { return; } var entityType = ((RefType)fromEnd.TypeUsage.EdmType).ElementType; List<RelProperty> propList; if (!_relPropertyMap.TryGetValue(entityType, out propList)) { propList = new List<RelProperty>(); _relPropertyMap[entityType] = propList; } propList.Add(prop); }
// effects: Returns a key correspnding to all the fields in different // ends of relationtype prefixed with "prefix" internal static ExtentKey GetKeyForRelationType(MemberPath prefix, AssociationType relationType) { var keyFields = new List<MemberPath>(); foreach (var endMember in relationType.AssociationEndMembers) { var endPrefix = new MemberPath(prefix, endMember); var entityType = MetadataHelper.GetEntityTypeForEnd(endMember); var primaryKey = GetPrimaryKeyForEntityType(endPrefix, entityType); keyFields.AddRange(primaryKey.KeyFields); } var key = new ExtentKey(keyFields); return key; }
/// <summary> /// Load all relationships in this entity container /// </summary> /// <param name="entityContainer"></param> internal void LoadRelationships(md.EntityContainer entityContainer) { // Check to see if I've already loaded information for this entity container if (m_entityContainerMap.ContainsKey(entityContainer)) { return; } // Load all relationships from this entitycontainer foreach (md.EntitySetBase e in entityContainer.BaseEntitySets) { md.RelationshipSet relationshipSet = e as md.RelationshipSet; if (relationshipSet == null) { continue; } // Relationship sets can only contain relationships md.RelationshipType relationshipType = (md.RelationshipType)relationshipSet.ElementType; md.AssociationType assocType = relationshipType as md.AssociationType; // // Handle only binary Association relationships for now // if (null == assocType || !IsBinary(relationshipType)) { continue; } foreach (md.ReferentialConstraint constraint in assocType.ReferentialConstraints) { List <ForeignKeyConstraint> fkConstraintList; ForeignKeyConstraint fkConstraint = new ForeignKeyConstraint(relationshipType, relationshipSet, constraint); if (!m_parentChildRelationships.TryGetValue(fkConstraint.Pair, out fkConstraintList)) { fkConstraintList = new List <ForeignKeyConstraint>(); m_parentChildRelationships[fkConstraint.Pair] = fkConstraintList; } // // Theoretically, we can have more than one fk constraint between // the 2 tables (though, it is unlikely) // fkConstraintList.Add(fkConstraint); } } // Mark this entity container as already loaded m_entityContainerMap[entityContainer] = entityContainer; }
private void CreateRelationships() { if (SessionData.ConventionBasedRelationshipsAreLoaded) { return; } SessionData.ConventionBasedRelationshipsAreLoaded = true; // find all the relationships foreach (AssociationType cspaceAssociation in SessionData.EdmItemCollection.GetItems <AssociationType>()) { Debug.Assert(cspaceAssociation.RelationshipEndMembers.Count == 2, "Relationships are assumed to have exactly two ends"); if (SessionData.CspaceToOspace.ContainsKey(cspaceAssociation)) { // don't try to load relationships that we already know about continue; } EdmType[] ospaceEndTypes = new EdmType[2]; if (SessionData.CspaceToOspace.TryGetValue(GetRelationshipEndType(cspaceAssociation.RelationshipEndMembers[0]), out ospaceEndTypes[0]) && SessionData.CspaceToOspace.TryGetValue(GetRelationshipEndType(cspaceAssociation.RelationshipEndMembers[1]), out ospaceEndTypes[1])) { Debug.Assert(ospaceEndTypes[0] is StructuralType); Debug.Assert(ospaceEndTypes[1] is StructuralType); // if we can find both ends of the relationship, then create it AssociationType ospaceAssociation = new AssociationType(cspaceAssociation.Name, cspaceAssociation.NamespaceName, cspaceAssociation.IsForeignKey, DataSpace.OSpace); for (int i = 0; i < cspaceAssociation.RelationshipEndMembers.Count; i++) { EntityType ospaceEndType = (EntityType)ospaceEndTypes[i]; RelationshipEndMember cspaceEnd = cspaceAssociation.RelationshipEndMembers[i]; ospaceAssociation.AddKeyMember(new AssociationEndMember(cspaceEnd.Name, ospaceEndType.GetReferenceType(), cspaceEnd.RelationshipMultiplicity)); } CacheEntry.TypesInAssembly.Add(ospaceAssociation); SessionData.TypesInLoading.Add(ospaceAssociation.FullName, ospaceAssociation); SessionData.CspaceToOspace.Add(cspaceAssociation, ospaceAssociation); } } }
internal static AssociationEndMember GetEndThatShouldBeMappedToKey(AssociationType associationType) { //For 1:* and 1:0..1 associations, the end other than 1 i.e. either * or 0..1 ends need to be //mapped to key columns if (associationType.AssociationEndMembers.Any(it => it.RelationshipMultiplicity.Equals(RelationshipMultiplicity.One))) { { return(associationType.AssociationEndMembers.SingleOrDefault(it => ((it.RelationshipMultiplicity.Equals(RelationshipMultiplicity.Many)) || (it.RelationshipMultiplicity.Equals(RelationshipMultiplicity.ZeroOrOne))))); } } //For 0..1:* associations, * end must be mapped to key. else if (associationType.AssociationEndMembers.Any(it => (it.RelationshipMultiplicity.Equals(RelationshipMultiplicity.ZeroOrOne)))) { { return(associationType.AssociationEndMembers.SingleOrDefault(it => ((it.RelationshipMultiplicity.Equals(RelationshipMultiplicity.Many))))); } } return(null); }
public AssociationTypeEmitter(ClientApiGenerator generator, AssociationType associationType) : base(generator, associationType) { }
private string GetAssociationCreateScript(AssociationType a) { StringBuilder sql = new StringBuilder(); StringBuilder keySql = new StringBuilder(); if (a.IsForeignKey) { EntityType childType = (EntityType)a.ReferentialConstraints[0].ToProperties[0].DeclaringType; EntityType parentType = (EntityType)a.ReferentialConstraints[0].FromProperties[0].DeclaringType; sql.AppendLine(String.Format( "ALTER TABLE `{0}` ADD CONSTRAINT {1}", childType.Name, a.Name)); sql.Append("\t FOREIGN KEY ("); string delimiter = ""; foreach (EdmProperty p in a.ReferentialConstraints[0].ToProperties) { EdmMember member; if (!childType.KeyMembers.TryGetValue(p.Name, false, out member)) keySql.AppendLine(String.Format( "ALTER TABLE `{0}` ADD KEY (`{1}`);", childType.Name, p.Name)); sql.AppendFormat("{0}{1}", delimiter, p.Name); delimiter = ", "; } sql.AppendLine(")"); delimiter = ""; sql.Append(String.Format("\tREFERENCES {0} (", parentType.Name)); foreach (EdmProperty p in a.ReferentialConstraints[0].FromProperties) { EdmMember member; if (!parentType.KeyMembers.TryGetValue(p.Name, false, out member)) keySql.AppendLine(String.Format( "ALTER TABLE `{0}` ADD KEY (`{1}`);", parentType.Name, p.Name)); sql.AppendFormat("{0}{1}", delimiter, p.Name); delimiter = ", "; } sql.AppendLine(");"); sql.AppendLine(); } keySql.Append(sql.ToString()); return keySql.ToString(); }
private string GetAssociationCreateScript(AssociationType a) { StringBuilder sql = new StringBuilder(); StringBuilder keySql = new StringBuilder(); if (a.IsForeignKey) { EntityType childType = (EntityType)a.ReferentialConstraints[0].ToProperties[0].DeclaringType; EntityType parentType = (EntityType)a.ReferentialConstraints[0].FromProperties[0].DeclaringType; string fkName = a.Name; if (fkName.Length > 64) { fkName = "FK_" + Guid.NewGuid().ToString().Replace("-", ""); } sql.AppendLine(String.Format( "ALTER TABLE `{0}` ADD CONSTRAINT {1}", _pluralizedNames[ childType.Name ], fkName)); sql.Append("\t FOREIGN KEY ("); string delimiter = ""; foreach (EdmProperty p in a.ReferentialConstraints[0].ToProperties) { EdmMember member; if (!childType.KeyMembers.TryGetValue(p.Name, false, out member)) keySql.AppendLine(String.Format( "ALTER TABLE `{0}` ADD KEY (`{1}`);", _pluralizedNames[childType.Name], p.Name)); sql.AppendFormat("{0}{1}", delimiter, p.Name); delimiter = ", "; } sql.AppendLine(")"); delimiter = ""; sql.Append(String.Format("\tREFERENCES {0} (", _pluralizedNames[parentType.Name])); foreach (EdmProperty p in a.ReferentialConstraints[0].FromProperties) { EdmMember member; if (!parentType.KeyMembers.TryGetValue(p.Name, false, out member)) keySql.AppendLine(String.Format( "ALTER TABLE `{0}` ADD KEY (`{1}`);", _pluralizedNames[parentType.Name], p.Name)); sql.AppendFormat("{0}{1}", delimiter, p.Name); delimiter = ", "; } sql.AppendLine(")"); OperationAction oa = a.AssociationEndMembers[0].DeleteBehavior; sql.AppendLine(String.Format(" ON DELETE {0} ON UPDATE {1};", oa == OperationAction.None ? "NO ACTION" : oa.ToString(), "NO ACTION")); sql.AppendLine(); } keySql.Append(sql.ToString()); return keySql.ToString(); }
static internal bool IsFkPartiallyContainedInPK(AssociationType association, out string errorMessage) { ReferentialConstraint constraint = association.ReferentialConstraints[0]; EntityType toType = (EntityType)constraint.ToProperties[0].DeclaringType; bool toPropertiesAreFullyContainedInPk = true; bool toPropertiesContainedAtLeastOnePK = false; foreach (EdmProperty edmProperty in constraint.ToProperties) { // check if there is at least one to property is not primary key toPropertiesAreFullyContainedInPk &= toType.KeyMembers.Contains(edmProperty); // check if there is one to property is primary key toPropertiesContainedAtLeastOnePK |= toType.KeyMembers.Contains(edmProperty); } if (!toPropertiesAreFullyContainedInPk && toPropertiesContainedAtLeastOnePK) { string foreignKeys = MetadataUtil.MembersToCommaSeparatedString((System.Collections.IEnumerable)constraint.ToProperties); string primaryKeys = MetadataUtil.MembersToCommaSeparatedString((System.Collections.IEnumerable)toType.KeyMembers); errorMessage = Strings.UnsupportedForeignKeyPattern(association.Name, foreignKeys, primaryKeys, toType.Name); return true; } errorMessage = ""; return false; }
internal void ResolveNavigationProperty(StructuralType declaringType, PropertyInfo propertyInfo) { Debug.Assert(propertyInfo.IsDefined(typeof(EdmRelationshipNavigationPropertyAttribute), false), "The property must have navigation property defined"); // EdmScalarPropertyAttribute, EdmComplexPropertyAttribute and EdmRelationshipNavigationPropertyAttribute // are all EdmPropertyAttributes that we need to process. If the current property is not an EdmPropertyAttribute // we will just ignore it and skip to the next property. object[] relationshipPropertyAttributes = propertyInfo.GetCustomAttributes(typeof(EdmRelationshipNavigationPropertyAttribute), false); Debug.Assert(relationshipPropertyAttributes.Length == 1, "There should be exactly one property for every navigation property"); // The only valid return types from navigation properties are: // (1) EntityType // (2) CollectionType containing valid EntityType // If TryGetLoadedType returned false, it could mean that we couldn't validate any part of the type, or it could mean that it's a generic // where the main generic type was validated, but the generic type parameter was not. We can't tell the difference, so just fail // with the same error message in both cases. The user will have to figure out which part of the type is wrong. // We can't just rely on checking for a generic because it can lead to a scenario where we report that the type parameter is invalid // when really it's the main generic type. That is more confusing than reporting the full name and letting the user determine the problem. EdmType propertyType; if (!TryGetLoadedType(propertyInfo.PropertyType, out propertyType) || !(propertyType.BuiltInTypeKind == BuiltInTypeKind.EntityType || propertyType.BuiltInTypeKind == BuiltInTypeKind.CollectionType)) { // Once an error is detected the property does not need to be validated further, just add to the errors // collection and continue with the next property. The failure will cause an exception to be thrown later during validation of all of the types. SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_InvalidNavPropReturnType(propertyInfo.Name, propertyInfo.DeclaringType.FullName, propertyInfo.PropertyType.FullName), null)); return; } // else we have a valid EntityType or CollectionType that contains EntityType. ResolveNonSchemaType enforces that a collection type // must contain an EntityType, and if it doesn't, propertyType will be null here. If propertyType is EntityType or CollectionType we know it is valid // Expecting EdmRelationshipNavigationPropertyAttribute to have AllowMultiple=False, so only look at first element in the attribute array EdmRelationshipNavigationPropertyAttribute attribute = (EdmRelationshipNavigationPropertyAttribute)relationshipPropertyAttributes[0]; EdmMember member = null; EdmType type; if (SessionData.TypesInLoading.TryGetValue(attribute.RelationshipNamespaceName + "." + attribute.RelationshipName, out type) && Helper.IsAssociationType(type)) { AssociationType relationshipType = (AssociationType)type; if (relationshipType != null) { // The return value of this property has been verified, so create the property now NavigationProperty navigationProperty = new NavigationProperty(propertyInfo.Name, TypeUsage.Create(propertyType), propertyInfo); navigationProperty.RelationshipType = relationshipType; member = navigationProperty; if (relationshipType.Members[0].Name == attribute.TargetRoleName) { navigationProperty.ToEndMember = (RelationshipEndMember)relationshipType.Members[0]; navigationProperty.FromEndMember = (RelationshipEndMember)relationshipType.Members[1]; } else if (relationshipType.Members[1].Name == attribute.TargetRoleName) { navigationProperty.ToEndMember = (RelationshipEndMember)relationshipType.Members[1]; navigationProperty.FromEndMember = (RelationshipEndMember)relationshipType.Members[0]; } else { SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.TargetRoleNameInNavigationPropertyNotValid( propertyInfo.Name, propertyInfo.DeclaringType.FullName, attribute.TargetRoleName, attribute.RelationshipName), navigationProperty)); member = null; } if (member != null && ((RefType)navigationProperty.FromEndMember.TypeUsage.EdmType).ElementType.ClrType != declaringType.ClrType) { SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NavigationPropertyRelationshipEndTypeMismatch( declaringType.FullName, navigationProperty.Name, relationshipType.FullName, navigationProperty.FromEndMember.Name, ((RefType)navigationProperty.FromEndMember.TypeUsage.EdmType).ElementType.ClrType), navigationProperty)); member = null; } } } else { SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.RelationshipNameInNavigationPropertyNotValid( propertyInfo.Name, propertyInfo.DeclaringType.FullName, attribute.RelationshipName), declaringType)); } if (member != null) { declaringType.AddMember(member); } }
/// <summary> /// Initializes a new instance of AssocationSet with the given name and the association type /// </summary> /// <param name="name">The name of the Assocation set</param> /// <param name="associationType">The association type of the entities that this associationship set type contains</param> internal AssociationSet(string name, AssociationType associationType) : base(name, null, null, null, associationType) { }
private KeyValuePair<string, RelationshipMultiplicity> CreateEndMultiplicityOverride(LoadMethodSessionState session, AssociationType storeAssociation, AssociationType modelAssociation) { // does the store have any constraints if (storeAssociation.ReferentialConstraints.Count == 0) { return new KeyValuePair<string, RelationshipMultiplicity>(); } ReferentialConstraint storeConstraint = storeAssociation.ReferentialConstraints[0]; //For foreign key associations, having any nullable columns will imply 0..1 //multiplicity, while for independent associations, all columns must be non-nullable for //0..1 association. bool nullableColumnsImplyingOneToOneMultiplicity = false; if (this.GenerateForeignKeyProperties) { nullableColumnsImplyingOneToOneMultiplicity = storeConstraint.ToProperties.All(p => p.Nullable == false); } else { nullableColumnsImplyingOneToOneMultiplicity = storeConstraint.ToProperties.Any(p => p.Nullable == false); } if (storeConstraint.FromRole.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne && storeConstraint.ToRole.RelationshipMultiplicity == RelationshipMultiplicity.Many && nullableColumnsImplyingOneToOneMultiplicity) { return new KeyValuePair<string, RelationshipMultiplicity>(storeConstraint.FromRole.Name, RelationshipMultiplicity.One); } return new KeyValuePair<string, RelationshipMultiplicity>(); }
private AssociationSet CreateModelAssociationSet(LoadMethodSessionState session, OneToOneMappingSerializer.CollapsedEntityAssociationSet collapsedAssociationSet) { // create the association string associationName = CreateModelName(collapsedAssociationSet.EntitySet.Name, session.UsedGlobalModelTypeNames); AssociationType association = new AssociationType(associationName, _namespaceName, false, DataSpace.CSpace); // create the association set string associationSetName = CreateModelName(collapsedAssociationSet.EntitySet.Name, session.UsedEntityContainerItemNames); AssociationSet set = new AssociationSet(associationSetName, association); // create the association and association set end members UniqueIdentifierService usedEndMemberNames = new UniqueIdentifierService(false); for(int i = 0; i < collapsedAssociationSet.AssociationSets.Count; i++) { AssociationSetEnd storeEnd; RelationshipMultiplicity multiplicity; OperationAction deleteBehavior; collapsedAssociationSet.GetStoreAssociationSetEnd(i, out storeEnd, out multiplicity, out deleteBehavior); AssociationEndMember end = CreateAssociationEndMember(session, storeEnd.CorrespondingAssociationEndMember, usedEndMemberNames, multiplicity, deleteBehavior); association.AddMember(end); EntitySet entitySet = session.MappingLookups.StoreEntitySetToModelEntitySet[storeEnd.EntitySet]; AssociationSetEnd setEnd = new AssociationSetEnd(entitySet, set, end); set.AddAssociationSetEnd(setEnd); session.MappingLookups.StoreAssociationSetEndToModelAssociationSetEnd.Add(storeEnd, setEnd); } // don't need a referential constraint CreateModelNavigationProperties(session, association); collapsedAssociationSet.ModelAssociationSet = set; return set; }
private static bool RequiresModelReferentialConstraint(AssociationType storeAssociation) { // does the store have any constraints if (storeAssociation.ReferentialConstraints.Count == 0) { return false; } ReferentialConstraint storeConstraint = storeAssociation.ReferentialConstraints[0]; Debug.Assert(storeConstraint.FromProperties.Count == storeConstraint.ToProperties.Count, "FromProperties and ToProperties have different counts"); Debug.Assert(storeConstraint.FromProperties.Count != 0, "No properties in the constraint, why does the constraint exist?"); Debug.Assert(storeConstraint.ToProperties[0].DeclaringType.BuiltInTypeKind == BuiltInTypeKind.EntityType, "The property is not from an EntityType"); EntityType toType = (EntityType)storeConstraint.ToProperties[0].DeclaringType; return RequiresModelReferentialConstraint(storeConstraint, toType); }
protected override void Visit(AssociationType associationType) { int index; if (!this.AddObjectToSeenListAndHashBuilder(associationType, out index)) { return; } this.AddObjectStartDumpToHashBuilder(associationType, index); #region Inner data visit this.AddObjectContentToHashBuilder(associationType.Abstract); this.AddObjectContentToHashBuilder(associationType.Identity); // FullName, Namespace, and Name are all covered by Identity base.Visit(associationType); #endregion this.AddObjectEndDumpToHashBuilder(); }
protected virtual void Visit(AssociationType associationType) { foreach (var endMember in associationType.AssociationEndMembers) { Visit(endMember); } Visit(associationType.BaseType); foreach (var keyMember in associationType.KeyMembers) { Visit(keyMember); } foreach (var member in associationType.GetDeclaredOnlyMembers<EdmMember>()) { Visit(member); } foreach (var item in associationType.ReferentialConstraints) { Visit(item); } foreach (var item in associationType.RelationshipEndMembers) { Visit(item); } }
private bool CreateReferentialConstraint(LoadMethodSessionState session, AssociationType association, AssociationEndMember pkEnd, AssociationEndMember fkEnd, List<RelationshipDetailsRow> columns, List<EdmSchemaError> errors) { EdmProperty[] fromProperties = new EdmProperty[columns.Count]; EdmProperty[] toProperties = new EdmProperty[columns.Count]; EntityType pkEntityType = session.RelationshipEndTypeLookup[pkEnd]; EntityType fkEntityType = session.RelationshipEndTypeLookup[fkEnd]; for (int index = 0; index < columns.Count; index++) { EdmProperty property; if(!pkEntityType.Properties.TryGetValue(columns[index].PKColumn, false, out property)) { errors.Add( new EdmSchemaError( Strings.AssociationMissingKeyColumn( pkEntityType.Name, fkEntityType.Name, pkEntityType.Name + "." + columns[index].PKColumn), (int)ModelBuilderErrorCode.AssociationMissingKeyColumn, EdmSchemaErrorSeverity.Warning)); return false; } fromProperties[index] = property; if(!fkEntityType.Properties.TryGetValue(columns[index].FKColumn, false, out property)) { errors.Add( new EdmSchemaError( Strings.AssociationMissingKeyColumn( pkEntityType.Name, fkEntityType.Name, fkEntityType.Name + "." + columns[index].FKColumn), (int)ModelBuilderErrorCode.AssociationMissingKeyColumn, EdmSchemaErrorSeverity.Warning)); return false; } toProperties[index] = property; } ReferentialConstraint constraint = new ReferentialConstraint(pkEnd, fkEnd, fromProperties, toProperties); association.AddReferentialConstraint(constraint); return true; }
private void CreateAssociationType(LoadMethodSessionState session, List<RelationshipDetailsRow> columns) { Debug.Assert(columns.Count != 0, "should have at least one column"); RelationshipDetailsRow firstRow = columns[0]; // get the entity types for the ends EntityType pkEntityType; EntityType fkEntityType; if (!TryGetEndEntities(session, firstRow, out pkEntityType, out fkEntityType)) { return; } if (!AreRelationshipColumnsTheTypesEntireKey(pkEntityType, columns, r => r.PKColumn)) { session.AddErrorsForType(pkEntityType, new EdmSchemaError(Strings.UnsupportedDbRelationship(firstRow.RelationshipName), (int)ModelBuilderErrorCode.UnsupportedDbRelationship, EdmSchemaErrorSeverity.Warning)); return; } UniqueIdentifierService usedEndNames = new UniqueIdentifierService(false); // figure out the lower bound of the pk end bool someFkColmnsAreNullable; if (_targetEntityFrameworkVersion == EntityFrameworkVersions.Version1) { someFkColmnsAreNullable = AreAllFkKeyColumnsNullable(fkEntityType, columns); } else { someFkColmnsAreNullable = AreAnyFkKeyColumnsNullable(fkEntityType, columns); } RelationshipMultiplicity pkMultiplicity = someFkColmnsAreNullable ? RelationshipMultiplicity.ZeroOrOne : RelationshipMultiplicity.One; //Get the Delete Action for the end and set it. //The only DeleteAction we support is Cascade, ignor all others for now. OperationAction onDeleteAction = OperationAction.None; if (firstRow.RelationshipIsCascadeDelete) { onDeleteAction = OperationAction.Cascade; } AssociationEndMember pkEnd = CreateAssociationEnd( session, pkEntityType, pkMultiplicity, usedEndNames, onDeleteAction); RelationshipMultiplicity fkMultiplicity = RelationshipMultiplicity.Many; if ( !someFkColmnsAreNullable && AreRelationshipColumnsTheTypesEntireKey(fkEntityType, columns, r => r.FKColumn)) { // both the pk and fk side columns are the keys of their types // so this is a 1 to one relationship fkMultiplicity = RelationshipMultiplicity.ZeroOrOne; } AssociationEndMember fkEnd = CreateAssociationEnd(session, fkEntityType, fkMultiplicity, usedEndNames, OperationAction.None); // create the type string typeName = session.UsedTypeNames.AdjustIdentifier(firstRow.RelationshipName); AssociationType type = new AssociationType(typeName, _namespaceName, false, DataSpace.SSpace); type.AddMember(pkEnd); type.AddMember(fkEnd); List<EdmSchemaError> errors = new List<EdmSchemaError>(); bool isValid = CreateReferentialConstraint(session, type, pkEnd, fkEnd, columns, errors); string errorMessage; // We can skip most validation checks if the FKs are directly surfaced (since we can produce valid mappings in these cases). if (!this.GenerateForeignKeyProperties) { if (IsFkPartiallyContainedInPK(type, out errorMessage)) { errors.Add(new EdmSchemaError( errorMessage, (int)ModelBuilderErrorCode.UnsupportedForeinKeyPattern, EdmSchemaErrorSeverity.Warning)); isValid = false; } if (isValid) { //Now check if any FK (which could also be a PK) is shared among multiple Associations (ie shared via foreign key constraint). // To do this we check if the Association Type being generated has any dependent property which is also a dependent in one of the association typed already added. //If so, we keep one Association and throw the rest away. foreach (var toPropertyOfAddedAssociation in session.AssociationTypes.SelectMany(t => t.ReferentialConstraints.SelectMany(refconst => refconst.ToProperties))) { foreach (var toProperty in type.ReferentialConstraints.SelectMany(refconst => refconst.ToProperties)) { if (toProperty.DeclaringType.Equals(toPropertyOfAddedAssociation.DeclaringType) && toProperty.Equals(toPropertyOfAddedAssociation)) { errors.Add(new EdmSchemaError( Strings.SharedForeignKey(type.Name, toProperty, toProperty.DeclaringType), (int)ModelBuilderErrorCode.SharedForeignKey, EdmSchemaErrorSeverity.Warning)); isValid = false; break; } } if (!isValid) { break; } } } } if (isValid) { session.AssociationTypes.Add(type); } else { session.InvalidTypes.Add(type); session.RelationshipEndTypeLookup.Remove(pkEnd); session.RelationshipEndTypeLookup.Remove(fkEnd); } type.SetReadOnly(); session.AddErrorsForType(type, errors); }
private AssociationSet CreateAssociationSet(LoadMethodSessionState session, AssociationType type) { AssociationSet set = new AssociationSet(type.Name, type); foreach(AssociationEndMember end in type.RelationshipEndMembers) { EntitySet entitySet = session.GetEntitySet(end); DbObjectKey key = session.GetKey(entitySet.ElementType); AssociationSetEnd setEnd = new AssociationSetEnd(entitySet, set, end); set.AddAssociationSetEnd(setEnd); } set.SetReadOnly(); return set; }
private void CreateModelNavigationProperties(LoadMethodSessionState session, AssociationType association) { Debug.Assert(association.Members.Count == 2, "this code assumes two ends"); AssociationEndMember end1 = (AssociationEndMember)association.Members[0]; AssociationEndMember end2 = (AssociationEndMember)association.Members[1]; CreateModelNavigationProperty(session, end1, end2); CreateModelNavigationProperty(session, end2, end1); }
private AssociationType CreateModelAssociationType(LoadMethodSessionState session, AssociationType storeAssociationType) { UniqueIdentifierService usedEndMemberNames = new UniqueIdentifierService(false); string name = CreateModelName(storeAssociationType.Name, session.UsedGlobalModelTypeNames); bool isFkAssociation = false; if (_targetEntityFrameworkVersion > EntityFrameworkVersions.Version1) { isFkAssociation = this.GenerateForeignKeyProperties || RequiresModelReferentialConstraint(storeAssociationType); } AssociationType association = new AssociationType(name, _namespaceName, isFkAssociation, DataSpace.CSpace); KeyValuePair<string, RelationshipMultiplicity> endMultiplicityOverride = CreateEndMultiplicityOverride(session, storeAssociationType, association); foreach (AssociationEndMember storeEndMember in storeAssociationType.RelationshipEndMembers) { AssociationEndMember end = CreateAssociationEndMember(session, storeEndMember, endMultiplicityOverride, usedEndMemberNames); session.MappingLookups.StoreAssociationEndMemberToModelAssociationEndMember.Add(storeEndMember, end); association.AddMember(end); } ReferentialConstraint constraint = CreateReferentialConstraint(session, storeAssociationType); if (constraint != null) { association.AddReferentialConstraint(constraint); } CreateModelNavigationProperties(session, association); return association; }
/// <summary> /// This method loads all the relationship type that this entity takes part in /// </summary> /// <param name="entityType"></param> /// <param name="context"></param> private void LoadRelationshipTypes() { foreach (EdmRelationshipAttribute roleAttribute in SourceAssembly.GetCustomAttributes(typeof(EdmRelationshipAttribute), false /*inherit*/)) { // Check if there is an entry already with this name if (TryFindNullParametersInRelationshipAttribute(roleAttribute)) { // don't give more errors for these same bad parameters continue; } bool errorEncountered = false; // return error if the role names are the same if (roleAttribute.Role1Name == roleAttribute.Role2Name) { SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.SameRoleNameOnRelationshipAttribute(roleAttribute.RelationshipName, roleAttribute.Role2Name), null)); errorEncountered = true; } if (!errorEncountered) { AssociationType associationType = new AssociationType(roleAttribute.RelationshipName, roleAttribute.RelationshipNamespaceName, roleAttribute.IsForeignKey, DataSpace.OSpace); SessionData.TypesInLoading.Add(associationType.FullName, associationType); TrackClosure(roleAttribute.Role1Type); TrackClosure(roleAttribute.Role2Type); // prevent lifting of loop vars string r1Name = roleAttribute.Role1Name; Type r1Type = roleAttribute.Role1Type; RelationshipMultiplicity r1Multiplicity = roleAttribute.Role1Multiplicity; AddTypeResolver(() => ResolveAssociationEnd(associationType, r1Name, r1Type, r1Multiplicity)); // prevent lifting of loop vars string r2Name = roleAttribute.Role2Name; Type r2Type = roleAttribute.Role2Type; RelationshipMultiplicity r2Multiplicity = roleAttribute.Role2Multiplicity; AddTypeResolver(() => ResolveAssociationEnd(associationType, r2Name, r2Type, r2Multiplicity)); // get assembly entry and add association type to the list of types in the assembly Debug.Assert(!CacheEntry.ContainsType(associationType.FullName), "Relationship type must not be present in the list of types"); CacheEntry.TypesInAssembly.Add(associationType); } } }
private ReferentialConstraint CreateReferentialConstraint(LoadMethodSessionState session, AssociationType storeAssociation) { Debug.Assert(session != null, "session parameter is null"); Debug.Assert(storeAssociation != null, "storeAssociation parameter is null"); Debug.Assert(storeAssociation.ReferentialConstraints.Count <= 1, "We don't have a reason to have more than one constraint yet"); // does the store have any constraints if (storeAssociation.ReferentialConstraints.Count == 0) { return null; } ReferentialConstraint storeConstraint = storeAssociation.ReferentialConstraints[0]; Debug.Assert(storeConstraint.FromProperties.Count == storeConstraint.ToProperties.Count, "FromProperties and ToProperties have different counts"); Debug.Assert(storeConstraint.FromProperties.Count != 0, "No properties in the constraint, why does the constraint exist?"); Debug.Assert(storeConstraint.ToProperties[0].DeclaringType.BuiltInTypeKind == BuiltInTypeKind.EntityType, "The property is not from an EntityType"); EntityType toType = (EntityType)storeConstraint.ToProperties[0].DeclaringType; // If we are generating foreign keys, there is always a referential constraint. Otherwise, check // if the dependent end includes key properties. If so, this implies that there is a referential // constraint. Otherwise, it is assumed that the foreign key properties are not defined in the // entity (verified ealier). if (!this.GenerateForeignKeyProperties && !RequiresModelReferentialConstraint(storeConstraint, toType)) { return null; } // we need a constraint so lets build it int count = storeConstraint.FromProperties.Count; EdmProperty[] fromProperties = new EdmProperty[count]; EdmProperty[] toProperties = new EdmProperty[count]; AssociationEndMember fromRole = session.MappingLookups.StoreAssociationEndMemberToModelAssociationEndMember[(AssociationEndMember)storeConstraint.FromRole]; AssociationEndMember toRole = session.MappingLookups.StoreAssociationEndMemberToModelAssociationEndMember[(AssociationEndMember)storeConstraint.ToRole]; for (int index = 0; index < count; index++) { fromProperties[index] = session.MappingLookups.StoreEdmPropertyToModelEdmProperty[storeConstraint.FromProperties[index]]; toProperties[index] = session.MappingLookups.StoreEdmPropertyToModelEdmProperty[storeConstraint.ToProperties[index]]; } ReferentialConstraint newConstraint = new ReferentialConstraint( fromRole, toRole, fromProperties, toProperties); return newConstraint; }
private void ResolveAssociationEnd(AssociationType associationType, string roleName, Type clrType, RelationshipMultiplicity multiplicity) { EntityType entityType; if (!TryGetRelationshipEndEntityType(clrType, out entityType)) { SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.RoleTypeInEdmRelationshipAttributeIsInvalidType(associationType.Name, roleName, clrType), null)); return; } associationType.AddKeyMember(new AssociationEndMember(roleName, entityType.GetReferenceType(), multiplicity)); }
/// <summary> /// Determines whether the specified { from, to } relationship end pairing represents a navigation that is /// valid for a relationship span sourced by an instance of the specified entity type. /// </summary> /// <param name="compareType">The Entity type which valid 'from' ends must reference (or a supertype of that Entity type)</param> /// <param name="associationType">The Association type to consider.</param> /// <param name="fromEnd">The candidate 'from' end, which will be checked based on the Entity type it references</param> /// <param name="toEnd">The candidate 'to' end, which will be checked base on the upper bound of its multiplicity</param> /// <returns> /// <c>True</c> if the end pairing represents a valid navigation from an instance of the specified entity type /// to an association end with a multiplicity upper bound of at most 1; otherwise <c>false</c> /// </returns> private static bool IsValidRelationshipSpan(EntityType compareType, AssociationType associationType, AssociationEndMember fromEnd, AssociationEndMember toEnd) { // Only a relationship end with a multiplicity of AT MOST one may be // considered as the 'to' end, so that the cardinality of the result // of the relationship span has an upper bound of 1. // Therefore ends with RelationshipMultiplicity of EITHER One OR ZeroOrOne // are the only ends that should be considered as target ends. // Note that a relationship span can be sourced by an Entity that is of the same type // as the Entity type referenced by the 'from' end OR any type in the same branch of // the type hierarchy. // // For example, in the following hierarchy: // // A (*<-->?) AOwner // |_B (*<-->1) BOwner // |_A1 (*<-->?) A1Owner // |_A2 // |_A3_1 (1<-->?) A3_1Owner // |_A3_2 (*<-->1) A3_2Owner // // An instance of 'A' would need ALL the 'AOwner', 'BOwner', 'A1Owner', 'A3_1Owner' and 'A3_2Owner' ends // spanned in because an instance of 'A' could actually be an instance of A, B, A1, A2, A3_1 or A3_2. // An instance of 'B' would only need 'AOwner' and 'BOwner' spanned in. // An instance of A2 would need 'AOwner', 'A1Owner', 'A3_1Owner' and 'A3_2Owner' spanned in. // An instance of A3_1 would only need 'AOwner', 'A1Owner' and 'A3_1Owner' spanned in. // // In general, the rule for relationship span is: // - 'To' end cardinality AT MOST one // AND // - Referenced Entity type of 'From' end is equal to instance Entity type // OR // - Referenced Entity type of 'From' end is a supertype of instance Entity type // OR // - Referenced Entity type of 'From' end is a subtype of instance Entity type // (this follows from the fact that an instance of 'A' may be an instance of any of its derived types. // Navigation for a subtype relationship will return null if the Entity instance navigation source // is not actually of the required subtype). // if(!associationType.IsForeignKey && (RelationshipMultiplicity.One == toEnd.RelationshipMultiplicity || RelationshipMultiplicity.ZeroOrOne == toEnd.RelationshipMultiplicity)) { EntityType fromEntityType = (EntityType)((RefType)fromEnd.TypeUsage.EdmType).ElementType; return (ObjectSpanRewriter.EntityTypeEquals(compareType, fromEntityType) || TypeSemantics.IsSubTypeOf(compareType, fromEntityType) || TypeSemantics.IsSubTypeOf(fromEntityType, compareType)); } return false; }
private void CreateRelationships() { if (SessionData.ConventionBasedRelationshipsAreLoaded) { return; } SessionData.ConventionBasedRelationshipsAreLoaded = true; // find all the relationships foreach (AssociationType cspaceAssociation in SessionData.EdmItemCollection.GetItems<AssociationType>()) { Debug.Assert(cspaceAssociation.RelationshipEndMembers.Count == 2, "Relationships are assumed to have exactly two ends"); if (SessionData.CspaceToOspace.ContainsKey(cspaceAssociation)) { // don't try to load relationships that we already know about continue; } EdmType[] ospaceEndTypes = new EdmType[2]; if (SessionData.CspaceToOspace.TryGetValue(GetRelationshipEndType(cspaceAssociation.RelationshipEndMembers[0]), out ospaceEndTypes[0]) && SessionData.CspaceToOspace.TryGetValue(GetRelationshipEndType(cspaceAssociation.RelationshipEndMembers[1]), out ospaceEndTypes[1])) { Debug.Assert(ospaceEndTypes[0] is StructuralType); Debug.Assert(ospaceEndTypes[1] is StructuralType); // if we can find both ends of the relationship, then create it AssociationType ospaceAssociation = new AssociationType(cspaceAssociation.Name, cspaceAssociation.NamespaceName, cspaceAssociation.IsForeignKey, DataSpace.OSpace); for (int i = 0; i < cspaceAssociation.RelationshipEndMembers.Count; i++) { EntityType ospaceEndType = (EntityType)ospaceEndTypes[i]; RelationshipEndMember cspaceEnd = cspaceAssociation.RelationshipEndMembers[i]; ospaceAssociation.AddKeyMember(new AssociationEndMember(cspaceEnd.Name, ospaceEndType.GetReferenceType(), cspaceEnd.RelationshipMultiplicity)); } CacheEntry.TypesInAssembly.Add(ospaceAssociation); SessionData.TypesInLoading.Add(ospaceAssociation.FullName, ospaceAssociation); SessionData.CspaceToOspace.Add(cspaceAssociation, ospaceAssociation); } } }
/// <summary> /// True if the specified association type is an identifying relationship. /// In order to be an identifying relationship, the association must have a referential constraint where all of the dependent properties are part of the dependent type's primary key. /// </summary> public bool IsIdentifyingRelationship(AssociationType association) { if (association == null) { throw new ArgumentNullException("association"); } return IsPrincipalEndOfIdentifyingRelationship(association.AssociationEndMembers[0]) || IsPrincipalEndOfIdentifyingRelationship(association.AssociationEndMembers[1]); }
/// <summary> /// Converts an association type from SOM to metadata /// </summary> /// <param name="element">The SOM element to process</param> /// <param name="providerManifest">The provider manifest to be used for conversion</param> /// <param name="convertedItemCache">The item collection for currently existing metadata objects</param> /// <param name="newGlobalItems">The new GlobalItem objects that are created as a result of this conversion</param> /// <returns>The association type object resulting from the convert</returns> private static AssociationType ConvertToAssociationType(Som.Relationship element, DbProviderManifest providerManifest, ConversionCache convertedItemCache, Dictionary<Som.SchemaElement, GlobalItem> newGlobalItems) { Debug.Assert(element.RelationshipKind == RelationshipKind.Association); AssociationType associationType = new AssociationType(element.Name, element.Namespace, element.IsForeignKey, GetDataSpace(providerManifest)); newGlobalItems.Add(element, associationType); foreach (Som.RelationshipEnd end in element.Ends) { Som.SchemaType entityTypeElement = end.Type; EntityType endEntityType = (EntityType)LoadSchemaElement(entityTypeElement, providerManifest, convertedItemCache, newGlobalItems); AssociationEndMember endMember = InitializeAssociationEndMember(associationType, end, endEntityType); AddOtherContent(end, endMember); // Loop through and convert the operations foreach (Som.OnOperation operation in end.Operations) { // Process only the ones that we recognize if (operation.Operation != Som.Operation.Delete) { continue; } // Determine the action for this operation OperationAction action = OperationAction.None; switch (operation.Action) { case Som.Action.Cascade: action = OperationAction.Cascade; break; case Som.Action.None: action = OperationAction.None; break; default: Debug.Fail("Operation action not supported."); break; } endMember.DeleteBehavior = action; } // Extract optional Documentation from the end element if (end.Documentation != null) { endMember.Documentation = ConvertToDocumentation(end.Documentation); } } Debug.Assert(associationType.ReferentialConstraints.Count == 0, "This must never have been initialized"); for (int i = 0; i < element.Constraints.Count; i++) { Som.ReferentialConstraint constraint = element.Constraints[i]; AssociationEndMember fromMember = (AssociationEndMember)associationType.Members[constraint.PrincipalRole.Name]; AssociationEndMember toMember = (AssociationEndMember)associationType.Members[constraint.DependentRole.Name]; EntityTypeBase fromEntityType = ((RefType)fromMember.TypeUsage.EdmType).ElementType; EntityTypeBase toEntityType = ((RefType)toMember.TypeUsage.EdmType).ElementType; ReferentialConstraint referentialConstraint = new ReferentialConstraint(fromMember, toMember, GetProperties(fromEntityType, constraint.PrincipalRole.RoleProperties), GetProperties(toEntityType, constraint.DependentRole.RoleProperties)); // Attach the optional Documentation if (constraint.Documentation != null) referentialConstraint.Documentation = ConvertToDocumentation(constraint.Documentation); if (constraint.PrincipalRole.Documentation != null) referentialConstraint.FromRole.Documentation = ConvertToDocumentation(constraint.PrincipalRole.Documentation); if (constraint.DependentRole.Documentation != null) referentialConstraint.ToRole.Documentation = ConvertToDocumentation(constraint.DependentRole.Documentation); associationType.AddReferentialConstraint(referentialConstraint); AddOtherContent(element.Constraints[i], referentialConstraint); } // Extract the optional Documentation if (element.Documentation != null) { associationType.Documentation = ConvertToDocumentation(element.Documentation); } AddOtherContent(element, associationType); return associationType; }
public bool TryGetNavigationPropertyAssociationType(string relationshipName, string targetRoleName, out AssociationType associationType) { return _navigationPropertyAssociationTypes.TryGetValue(new Tuple<string, string>(relationshipName, targetRoleName), out associationType); }
/// <summary> /// Initialize the end member if its not initialized already /// </summary> /// <param name="associationType"></param> /// <param name="end"></param> /// <param name="endMemberType"></param> private static AssociationEndMember InitializeAssociationEndMember(AssociationType associationType, Som.IRelationshipEnd end, EntityType endMemberType) { AssociationEndMember associationEnd; EdmMember member; // make sure that the end is not initialized as of yet if (!associationType.Members.TryGetValue(end.Name, false/*ignoreCase*/, out member)) { // Create the end member and add the operations associationEnd = new AssociationEndMember(end.Name, endMemberType.GetReferenceType(), end.Multiplicity.Value); associationType.AddKeyMember(associationEnd); } else { associationEnd = (AssociationEndMember)member; } //Extract the optional Documentation Som.RelationshipEnd relationshipEnd = end as Som.RelationshipEnd; if (relationshipEnd != null && (relationshipEnd.Documentation != null)) { associationEnd.Documentation = ConvertToDocumentation(relationshipEnd.Documentation); } return associationEnd; }
/// <summary> /// Construct the new AssociationTypeMapping object. /// </summary> /// <param name="relation">Represents the Association Type metadata object</param> /// <param name="setMapping">Set Mapping that contains this Type mapping </param> internal StorageAssociationTypeMapping(AssociationType relation, StorageSetMapping setMapping) : base(setMapping) { this.m_relation = relation; }
/// <summary> /// A mechanism to lookup AssociationType metadata for proxies for a given entity and association information /// </summary> /// <param name="wrappedEntity">The entity instance used to lookup the proxy type</param> /// <param name="relationshipName">The name of the relationship (FullName or Name)</param> /// <param name="targetRoleName">Target role of the relationship</param> /// <param name="associationType">The AssociationType for that property</param> /// <returns>True if an AssociationType is found in proxy metadata, false otherwise</returns> internal static bool TryGetAssociationTypeFromProxyInfo(IEntityWrapper wrappedEntity, string relationshipName, string targetRoleName, out AssociationType associationType) { EntityProxyTypeInfo proxyInfo = null; associationType = null; return (EntityProxyFactory.TryGetProxyType(wrappedEntity.Entity.GetType(), out proxyInfo) && proxyInfo != null && proxyInfo.TryGetNavigationPropertyAssociationType(relationshipName, targetRoleName, out associationType)); }