/// <summary> /// Called after a navigation property that has an attribute is added to an entity type. /// </summary> /// <param name="relationshipBuilder"> The builder for the relationship. </param> /// <param name="navigation"> The navigation. </param> /// <param name="attribute"> The attribute. </param> /// <param name="context"> Additional information associated with convention execution. </param> public override void ProcessNavigationAdded( IConventionRelationshipBuilder relationshipBuilder, IConventionNavigation navigation, RequiredAttribute attribute, IConventionContext <IConventionNavigation> context) { Check.NotNull(relationshipBuilder, nameof(relationshipBuilder)); Check.NotNull(navigation, nameof(navigation)); Check.NotNull(attribute, nameof(attribute)); if (navigation.IsCollection) { Dependencies.Logger.RequiredAttributeOnCollection(navigation.ForeignKey.DependentToPrincipal); return; } if (!navigation.IsOnDependent) { var inverse = navigation.Inverse; if (inverse != null) { var attributes = GetAttributes <RequiredAttribute>(inverse.DeclaringEntityType, inverse); if (attributes.Any()) { Dependencies.Logger.RequiredAttributeOnBothNavigations(navigation, inverse); return; } } if (relationshipBuilder.Metadata.GetPrincipalEndConfigurationSource() != null) { Dependencies.Logger.RequiredAttributeOnDependent(navigation.ForeignKey.PrincipalToDependent); return; } var newRelationshipBuilder = relationshipBuilder.HasEntityTypes( relationshipBuilder.Metadata.DeclaringEntityType, relationshipBuilder.Metadata.PrincipalEntityType); if (newRelationshipBuilder == null) { return; } Dependencies.Logger.RequiredAttributeInverted(newRelationshipBuilder.Metadata.DependentToPrincipal); relationshipBuilder = newRelationshipBuilder; } relationshipBuilder.IsRequired(true, fromDataAnnotation: true); context.StopProcessingIfChanged(relationshipBuilder.Metadata.DependentToPrincipal); }
/// <summary> /// Called after a navigation is added to the entity type. /// </summary> /// <param name="relationshipBuilder"> The builder for the foreign key. </param> /// <param name="navigation"> The navigation. </param> /// <param name="context"> Additional information associated with convention execution. </param> public virtual void ProcessNavigationAdded( IConventionRelationshipBuilder relationshipBuilder, IConventionNavigation navigation, IConventionContext <IConventionNavigation> context) { Check.NotNull(relationshipBuilder, nameof(relationshipBuilder)); Check.NotNull(navigation, nameof(navigation)); var modelBuilder = relationshipBuilder.ModelBuilder; if (!IsNonNullable(modelBuilder, navigation) || navigation.IsCollection()) { return; } if (!navigation.IsDependentToPrincipal()) { var inverse = navigation.FindInverse(); if (inverse != null) { if (IsNonNullable(modelBuilder, inverse)) { Dependencies.Logger.NonNullableReferenceOnBothNavigations(navigation, inverse); return; } } if (!navigation.ForeignKey.IsUnique || relationshipBuilder.Metadata.GetPrincipalEndConfigurationSource() != null) { Dependencies.Logger.NonNullableReferenceOnDependent(navigation.ForeignKey.PrincipalToDependent); return; } var newRelationshipBuilder = relationshipBuilder.HasEntityTypes( relationshipBuilder.Metadata.DeclaringEntityType, relationshipBuilder.Metadata.PrincipalEntityType); if (newRelationshipBuilder == null) { return; } Dependencies.Logger.NonNullableInverted(newRelationshipBuilder.Metadata.DependentToPrincipal); relationshipBuilder = newRelationshipBuilder; } relationshipBuilder.IsRequired(true); context.StopProcessingIfChanged(relationshipBuilder.Metadata.DependentToPrincipal); }
private IConventionRelationshipBuilder DiscoverProperties( IConventionRelationshipBuilder relationshipBuilder, IConventionContext context) { var foreignKey = relationshipBuilder.Metadata; if (!ConfigurationSource.Convention.Overrides(foreignKey.GetPropertiesConfigurationSource())) { var batch = context.DelayConventions(); using (var foreignKeyReference = batch.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( newType, fkProperty.Name, fkProperty.GetConfigurationSource() == ConfigurationSource.DataAnnotation); } } } batch.Dispose(); return(foreignKeyReference.Object?.Builder); } } var invertible = 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); invertible = 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); if (invertedRelationshipBuilder != null) { return(invertedRelationshipBuilder); } } var foreignKeyProperties = FindCandidateForeignKeyProperties(relationshipBuilder.Metadata, onDependent: true); if (foreignKeyProperties == null) { if (invertible && ConfigurationSource.Convention.Overrides(foreignKey.GetPrincipalEndConfigurationSource())) { var candidatePropertiesOnPrincipal = FindCandidateForeignKeyProperties(foreignKey, onDependent: false); if (candidatePropertiesOnPrincipal != null) { var invertedRelationshipBuilder = relationshipBuilder .HasEntityTypes(foreignKey.DeclaringEntityType, foreignKey.PrincipalEntityType); var invertedFk = invertedRelationshipBuilder?.Metadata; if (invertedFk?.IsSelfReferencing() == true) { invertedRelationshipBuilder = invertedRelationshipBuilder.HasNavigations( invertedFk.PrincipalToDependent?.Name, invertedFk.DependentToPrincipal?.Name); } 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 (invertible) { foreignKeyProperties = FindCandidateForeignKeyProperties(foreignKey, onDependent: true, matchPk: true); var candidatePropertiesOnPrincipal = FindCandidateForeignKeyProperties(foreignKey, onDependent: false, matchPk: true); if (candidatePropertiesOnPrincipal != null) { if (foreignKeyProperties == null) { using (var batch = context.DelayConventions()) { var invertedRelationshipBuilder = relationshipBuilder .HasEntityTypes(foreignKey.DeclaringEntityType, foreignKey.PrincipalEntityType); return(batch.Run( invertedRelationshipBuilder.HasForeignKey(candidatePropertiesOnPrincipal).Metadata) ?.Builder); } } foreignKeyProperties = null; ((ForeignKey)relationshipBuilder.Metadata).SetPrincipalEndConfigurationSource(null); } } } if (foreignKeyProperties == null && invertible && ConfigurationSource.Convention.Overrides(foreignKey.GetPrincipalEndConfigurationSource())) { ((ForeignKey)relationshipBuilder.Metadata).SetPrincipalEndConfigurationSource(null); } } else if (invertible && ConfigurationSource.Convention.Overrides(foreignKey.GetPrincipalEndConfigurationSource())) { var candidatePropertiesOnPrincipal = FindCandidateForeignKeyProperties(foreignKey, onDependent: false); if (candidatePropertiesOnPrincipal != null) { // Principal end is ambiguous foreignKeyProperties = null; ((ForeignKey)relationshipBuilder.Metadata).SetPrincipalEndConfigurationSource(null); } } if (foreignKeyProperties == null) { return(((ForeignKey)foreignKey).Builder.ReuniquifyTemporaryProperties(false)); } var conflictingFKCount = foreignKey.DeclaringEntityType.FindForeignKeys(foreignKeyProperties) .Concat( foreignKey.DeclaringEntityType.GetDerivedTypes() .SelectMany(et => et.FindDeclaredForeignKeys(foreignKeyProperties))) .Count(); if (foreignKey.Properties.SequenceEqual(foreignKeyProperties)) { return(conflictingFKCount > 1 ? ((ForeignKey)foreignKey).Builder.ReuniquifyTemporaryProperties(true) : relationshipBuilder); } if (conflictingFKCount > 0) { return(((ForeignKey)foreignKey).Builder.ReuniquifyTemporaryProperties(false)); } var newRelationshipBuilder = relationshipBuilder.HasForeignKey(foreignKeyProperties); if (newRelationshipBuilder != null) { return(newRelationshipBuilder); } return(relationshipBuilder.Metadata.Builder == null ? null : relationshipBuilder); }