/// <summary> /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// </summary> public virtual InternalRelationshipBuilder Apply( InternalRelationshipBuilder relationshipBuilder, Navigation navigation) { Check.NotNull(relationshipBuilder, nameof(relationshipBuilder)); Check.NotNull(navigation, nameof(navigation)); if (!IsNonNullable(navigation) || navigation.IsCollection()) { return(relationshipBuilder); } if (!navigation.IsDependentToPrincipal()) { var inverse = navigation.FindInverse(); if (inverse != null) { if (IsNonNullable(inverse)) { Logger.NonNullableReferenceOnBothNavigations(navigation, inverse); return(relationshipBuilder); } } if (!navigation.ForeignKey.IsUnique || relationshipBuilder.Metadata.GetPrincipalEndConfigurationSource() != null) { return(relationshipBuilder); } var newRelationshipBuilder = relationshipBuilder.HasEntityTypes( relationshipBuilder.Metadata.DeclaringEntityType, relationshipBuilder.Metadata.PrincipalEntityType, ConfigurationSource.Convention); if (newRelationshipBuilder == null) { return(relationshipBuilder); } Logger.NonNullableOnDependent(newRelationshipBuilder.Metadata.DependentToPrincipal); relationshipBuilder = newRelationshipBuilder; } return(relationshipBuilder.IsRequired(true, ConfigurationSource.Convention) ?? relationshipBuilder); }
/// <summary> /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// </summary> public override InternalRelationshipBuilder Apply( InternalRelationshipBuilder relationshipBuilder, Navigation navigation, RequiredAttribute attribute) { Check.NotNull(relationshipBuilder, nameof(relationshipBuilder)); Check.NotNull(navigation, nameof(navigation)); Check.NotNull(attribute, nameof(attribute)); if (navigation.IsCollection()) { return(relationshipBuilder); } if (!navigation.IsDependentToPrincipal()) { var inverse = navigation.FindInverse(); if (inverse != null) { var attributes = GetAttributes <RequiredAttribute>(inverse.DeclaringEntityType, inverse.Name); if (attributes.Any()) { Logger.RequiredAttributeOnBothNavigations(navigation, inverse); return(relationshipBuilder); } } if (!navigation.ForeignKey.IsUnique || relationshipBuilder.Metadata.GetPrincipalEndConfigurationSource() != null) { return(relationshipBuilder); } var newRelationshipBuilder = relationshipBuilder.HasEntityTypes( relationshipBuilder.Metadata.DeclaringEntityType, relationshipBuilder.Metadata.PrincipalEntityType, ConfigurationSource.Convention); if (newRelationshipBuilder == null) { return(relationshipBuilder); } Logger.RequiredAttributeOnDependent(newRelationshipBuilder.Metadata.DependentToPrincipal); relationshipBuilder = newRelationshipBuilder; } return(relationshipBuilder.IsRequired(true, ConfigurationSource.DataAnnotation) ?? relationshipBuilder); }
private InternalRelationshipBuilder DiscoverProperties(InternalRelationshipBuilder relationshipBuilder) { var foreignKey = relationshipBuilder.Metadata; if (!ConfigurationSource.Convention.Overrides(foreignKey.GetPropertiesConfigurationSource())) { using (var batch = foreignKey.DeclaringEntityType.Model.ConventionDispatcher.StartBatch()) { using (var foreignKeyReference = foreignKey.DeclaringEntityType.Model.ConventionDispatcher.Tracker.Track(foreignKey)) { foreach (var fkProperty in foreignKey.Properties) { if (ConfigurationSource.Convention.Overrides(fkProperty.GetTypeConfigurationSource()) && fkProperty.IsShadowProperty() && fkProperty.ClrType.IsNullableType() == foreignKey.IsRequired && fkProperty.GetContainingForeignKeys().All(otherFk => otherFk.IsRequired == foreignKey.IsRequired)) { var newType = fkProperty.ClrType.MakeNullable(!foreignKey.IsRequired); if (fkProperty.ClrType != newType) { fkProperty.DeclaringEntityType.Builder.Property( fkProperty.ClrType.MakeNullable(!foreignKey.IsRequired), fkProperty.Name, fkProperty.GetConfigurationSource(), ConfigurationSource.Convention); } } } batch.Run(); return(foreignKeyReference.Object?.Builder); } } } var invertable = true; if (foreignKey.DeclaringEntityType.DefiningEntityType == foreignKey.PrincipalEntityType || foreignKey.IsOwnership || foreignKey.DeclaringEntityType.IsKeyless || (!foreignKey.IsUnique && !ConfigurationSource.Convention.Overrides(foreignKey.GetIsUniqueConfigurationSource())) || foreignKey.PrincipalToDependent?.IsCollection() == true || foreignKey.DeclaringEntityType.FindOwnership() != null) { relationshipBuilder = relationshipBuilder.HasEntityTypes( foreignKey.PrincipalEntityType, foreignKey.DeclaringEntityType, ConfigurationSource.Convention); invertable = false; } else if (ConfigurationSource.Convention.Overrides(foreignKey.GetPrincipalEndConfigurationSource()) && (foreignKey.PrincipalEntityType.DefiningEntityType == foreignKey.DeclaringEntityType || (foreignKey.PrincipalEntityType.FindOwnership() != null && foreignKey.PrincipalToDependent != null && foreignKey.DependentToPrincipal == null))) { var invertedRelationshipBuilder = relationshipBuilder.HasEntityTypes( foreignKey.DeclaringEntityType, foreignKey.PrincipalEntityType, ConfigurationSource.Convention); if (invertedRelationshipBuilder != null) { return(invertedRelationshipBuilder); } } var foreignKeyProperties = FindCandidateForeignKeyProperties(relationshipBuilder.Metadata, onDependent: true); if (foreignKeyProperties == null) { if (invertable && ConfigurationSource.Convention.Overrides(foreignKey.GetPrincipalEndConfigurationSource())) { var candidatePropertiesOnPrincipal = FindCandidateForeignKeyProperties(foreignKey, onDependent: false); if (candidatePropertiesOnPrincipal != null) { var invertedRelationshipBuilder = relationshipBuilder .HasEntityTypes(foreignKey.DeclaringEntityType, foreignKey.PrincipalEntityType, ConfigurationSource.Convention); var invertedFk = invertedRelationshipBuilder?.Metadata; if (invertedFk?.IsSelfReferencing() == true) { invertedRelationshipBuilder = invertedRelationshipBuilder.HasNavigations( invertedFk.PrincipalToDependent?.Name, invertedFk.DependentToPrincipal?.Name, ConfigurationSource.Convention); } return(invertedRelationshipBuilder ?? (foreignKey.Builder == null ? null : relationshipBuilder)); } } if (foreignKey.IsUnique && foreignKey.DeclaringEntityType.BaseType == null && !foreignKey.IsSelfReferencing()) { // Try to use PK properties if principal end is not ambiguous if (!foreignKey.IsOwnership && (!ConfigurationSource.Convention.Overrides(foreignKey.GetPrincipalEndConfigurationSource()) || foreignKey.DeclaringEntityType.DefiningEntityType == foreignKey.PrincipalEntityType)) { foreignKeyProperties = GetCompatiblePrimaryKeyProperties( foreignKey.DeclaringEntityType, foreignKey.PrincipalEntityType, foreignKey.PrincipalKey.Properties); } else if (invertable) { foreignKeyProperties = FindCandidateForeignKeyProperties(foreignKey, onDependent: true, matchPk: true); var candidatePropertiesOnPrincipal = FindCandidateForeignKeyProperties(foreignKey, onDependent: false, matchPk: true); if (candidatePropertiesOnPrincipal != null) { if (foreignKeyProperties == null) { using (var batch = foreignKey.DeclaringEntityType.Model.ConventionDispatcher.StartBatch()) { var invertedRelationshipBuilder = relationshipBuilder .HasEntityTypes( foreignKey.DeclaringEntityType, foreignKey.PrincipalEntityType, ConfigurationSource.Convention); return(batch.Run( invertedRelationshipBuilder.HasForeignKey( candidatePropertiesOnPrincipal, foreignKey.PrincipalEntityType, ConfigurationSource.Convention))); } } foreignKeyProperties = null; relationshipBuilder.Metadata.SetPrincipalEndConfigurationSource(null); } } } if (foreignKeyProperties == null && invertable && ConfigurationSource.Convention.Overrides(foreignKey.GetPrincipalEndConfigurationSource())) { relationshipBuilder.Metadata.SetPrincipalEndConfigurationSource(null); } } else if (invertable && ConfigurationSource.Convention.Overrides(foreignKey.GetPrincipalEndConfigurationSource())) { var candidatePropertiesOnPrincipal = FindCandidateForeignKeyProperties(foreignKey, onDependent: false); if (candidatePropertiesOnPrincipal != null) { // Principal end is ambiguous foreignKeyProperties = null; relationshipBuilder.Metadata.SetPrincipalEndConfigurationSource(null); } } if (foreignKeyProperties == null) { return(ReuniquifyTemporaryProperties(foreignKey, force: false)); } var conflictingFKCount = foreignKey.DeclaringEntityType.FindForeignKeysInHierarchy(foreignKeyProperties).Count(); if (foreignKey.Properties.SequenceEqual(foreignKeyProperties)) { return(conflictingFKCount > 1 ? ReuniquifyTemporaryProperties(foreignKey, force: true) : relationshipBuilder); } if (conflictingFKCount > 0) { return(ReuniquifyTemporaryProperties(foreignKey, force: false)); } var newRelationshipBuilder = relationshipBuilder.HasForeignKey(foreignKeyProperties, ConfigurationSource.Convention); if (newRelationshipBuilder != null) { return(newRelationshipBuilder); } return(relationshipBuilder.Metadata.Builder == null ? null : relationshipBuilder); }