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 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; }
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); }