private IConventionForeignKeyBuilder?ConfigureInverseNavigation( IConventionEntityTypeBuilder entityTypeBuilder, MemberInfo navigationMemberInfo, IConventionEntityTypeBuilder targetEntityTypeBuilder, InversePropertyAttribute attribute) { var entityType = entityTypeBuilder.Metadata; var targetEntityType = targetEntityTypeBuilder.Metadata; var targetClrType = targetEntityType.ClrType; var inverseNavigationPropertyInfo = targetEntityType.GetRuntimeProperties().Values .FirstOrDefault(p => string.Equals(p.GetSimpleMemberName(), attribute.Property)) ?? targetEntityType.GetRuntimeProperties().Values .FirstOrDefault(p => string.Equals(p.GetSimpleMemberName(), attribute.Property, StringComparison.OrdinalIgnoreCase)); if (inverseNavigationPropertyInfo == null || !Dependencies.MemberClassifier.GetNavigationCandidates(targetEntityType)[inverseNavigationPropertyInfo] .Type.IsAssignableFrom(entityType.ClrType)) { throw new InvalidOperationException( CoreStrings.InvalidNavigationWithInverseProperty( navigationMemberInfo.Name, entityType.DisplayName(), attribute.Property, targetClrType.ShortDisplayName())); } if (Equals(inverseNavigationPropertyInfo, navigationMemberInfo)) { throw new InvalidOperationException( CoreStrings.SelfReferencingNavigationWithInverseProperty( entityType.DisplayName(), navigationMemberInfo.Name)); } // Check for InversePropertyAttribute on the inverse navigation to verify that it matches. if (Attribute.IsDefined(inverseNavigationPropertyInfo, typeof(InversePropertyAttribute))) { var inverseAttribute = inverseNavigationPropertyInfo.GetCustomAttribute <InversePropertyAttribute>(true) !; if (inverseAttribute.Property != navigationMemberInfo.GetSimpleMemberName()) { throw new InvalidOperationException( CoreStrings.InversePropertyMismatch( navigationMemberInfo.Name, entityType.DisplayName(), inverseNavigationPropertyInfo.Name, targetEntityType.DisplayName())); } } var referencingNavigationsWithAttribute = AddInverseNavigation(entityType, navigationMemberInfo, targetEntityType, inverseNavigationPropertyInfo); if (TryRemoveIfAmbiguous( entityType, navigationMemberInfo, targetEntityType, targetEntityType.BaseType, inverseNavigationPropertyInfo, referencingNavigationsWithAttribute, out var conventionForeignKeyBuilder)) { return(conventionForeignKeyBuilder); } var ownership = entityType.FindOwnership(); if (ownership != null && ownership.PrincipalEntityType == targetEntityType && ownership.PrincipalToDependent?.GetIdentifyingMemberInfo() != inverseNavigationPropertyInfo) { Dependencies.Logger.NonOwnershipInverseNavigationWarning( entityType, navigationMemberInfo, targetEntityType, inverseNavigationPropertyInfo, ownership.PrincipalToDependent?.GetIdentifyingMemberInfo() !); return(null); } var targetOwnership = targetEntityType.FindOwnership(); if (targetOwnership != null && targetOwnership.PrincipalEntityType == entityType && targetOwnership.PrincipalToDependent?.GetIdentifyingMemberInfo() != navigationMemberInfo) { Dependencies.Logger.NonOwnershipInverseNavigationWarning( entityType, navigationMemberInfo, targetEntityType, inverseNavigationPropertyInfo, targetOwnership.PrincipalToDependent?.GetIdentifyingMemberInfo() !); return(null); } if (targetEntityType.IsOwned() && (targetOwnership == null || targetOwnership.PrincipalEntityType == entityType)) { if (navigationMemberInfo.DeclaringType != entityType.ClrType && (entityType.Model.FindEntityType(navigationMemberInfo.DeclaringType !) != null || (navigationMemberInfo.DeclaringType != entityType.ClrType.BaseType && entityType.Model.FindEntityType(entityType.ClrType.BaseType !) != null))) { return(null); } return(entityTypeBuilder.HasOwnership( targetEntityType, navigationMemberInfo, inverseNavigationPropertyInfo, fromDataAnnotation: true)); } if (entityType.IsOwned() && (ownership == null || ownership.PrincipalEntityType == targetEntityType)) { if (navigationMemberInfo.DeclaringType != entityType.ClrType && (entityType.Model.FindEntityType(navigationMemberInfo.DeclaringType !) != null || (navigationMemberInfo.DeclaringType != entityType.ClrType.BaseType && entityType.Model.FindEntityType(entityType.ClrType.BaseType !) != null))) { return(null); } return(targetEntityTypeBuilder.HasOwnership( entityTypeBuilder.Metadata, inverseNavigationPropertyInfo, navigationMemberInfo, fromDataAnnotation: true)); } if (ownership != null || targetOwnership != null) { return(null); } var newForeignKeyBuilder = targetEntityTypeBuilder.HasRelationship( entityType, inverseNavigationPropertyInfo, navigationMemberInfo, fromDataAnnotation: true); if (newForeignKeyBuilder == null && navigationMemberInfo is PropertyInfo navigationPropertyInfo) { var navigationTargetType = navigationPropertyInfo.PropertyType.TryGetSequenceType(); var inverseNavigationTargetType = inverseNavigationPropertyInfo.PropertyType.TryGetSequenceType(); if (navigationTargetType != null && inverseNavigationTargetType != null && navigationTargetType.IsAssignableFrom(targetClrType) && inverseNavigationTargetType.IsAssignableFrom(entityType.ClrType)) { entityTypeBuilder.HasSkipNavigation( navigationPropertyInfo, targetEntityType, inverseNavigationPropertyInfo, collections: true, onDependent: false, fromDataAnnotation: true); } } return(newForeignKeyBuilder); }
private IConventionRelationshipBuilder ConfigureInverseNavigation( IConventionEntityTypeBuilder entityTypeBuilder, MemberInfo navigationMemberInfo, IConventionEntityTypeBuilder targetEntityTypeBuilder, InversePropertyAttribute attribute) { var entityType = entityTypeBuilder.Metadata; var targetClrType = targetEntityTypeBuilder.Metadata.ClrType; var inverseNavigationPropertyInfo = targetEntityTypeBuilder.Metadata.GetRuntimeProperties().Values .FirstOrDefault(p => string.Equals(p.GetSimpleMemberName(), attribute.Property, StringComparison.OrdinalIgnoreCase)); if (inverseNavigationPropertyInfo == null || !Dependencies.MemberClassifier.FindCandidateNavigationPropertyType(inverseNavigationPropertyInfo).GetTypeInfo() .IsAssignableFrom(entityType.ClrType.GetTypeInfo())) { throw new InvalidOperationException( CoreStrings.InvalidNavigationWithInverseProperty( navigationMemberInfo.Name, entityType.DisplayName(), attribute.Property, targetClrType.ShortDisplayName())); } if (Equals(inverseNavigationPropertyInfo, navigationMemberInfo)) { throw new InvalidOperationException( CoreStrings.SelfReferencingNavigationWithInverseProperty( navigationMemberInfo.Name, entityType.DisplayName(), navigationMemberInfo.Name, entityType.DisplayName())); } // Check for InversePropertyAttribute on the inverse navigation to verify that it matches. if (Attribute.IsDefined(inverseNavigationPropertyInfo, typeof(InversePropertyAttribute))) { var inverseAttribute = inverseNavigationPropertyInfo.GetCustomAttribute <InversePropertyAttribute>(true); if (inverseAttribute.Property != navigationMemberInfo.GetSimpleMemberName()) { throw new InvalidOperationException( CoreStrings.InversePropertyMismatch( navigationMemberInfo.Name, entityType.DisplayName(), inverseNavigationPropertyInfo.Name, targetEntityTypeBuilder.Metadata.DisplayName())); } } var referencingNavigationsWithAttribute = AddInverseNavigation(entityType, navigationMemberInfo, targetEntityTypeBuilder.Metadata, inverseNavigationPropertyInfo); var ambiguousInverse = FindAmbiguousInverse( navigationMemberInfo, entityType, referencingNavigationsWithAttribute); var baseType = targetEntityTypeBuilder.Metadata.BaseType; while (ambiguousInverse == null && baseType != null) { var navigationMap = GetInverseNavigations(baseType); if (navigationMap != null && navigationMap.TryGetValue(inverseNavigationPropertyInfo.Name, out var inverseTuple)) { referencingNavigationsWithAttribute = inverseTuple.References; ambiguousInverse = FindAmbiguousInverse(navigationMemberInfo, entityType, referencingNavigationsWithAttribute); } baseType = baseType.BaseType; } if (ambiguousInverse != null) { var existingInverse = targetEntityTypeBuilder.Metadata.FindNavigation(inverseNavigationPropertyInfo)?.FindInverse(); var existingInverseType = existingInverse?.DeclaringEntityType; if (existingInverse != null && IsAmbiguousInverse( existingInverse.GetIdentifyingMemberInfo(), existingInverseType, referencingNavigationsWithAttribute)) { var fk = existingInverse.ForeignKey; if (fk.IsOwnership || fk.DeclaringEntityType.Builder.HasNoRelationship(fk, fromDataAnnotation: true) == null) { fk.Builder.HasNavigation( (string)null, existingInverse.IsDependentToPrincipal(), fromDataAnnotation: true); } } var existingNavigation = entityType.FindNavigation(navigationMemberInfo); if (existingNavigation != null) { var fk = existingNavigation.ForeignKey; if (fk.IsOwnership || fk.DeclaringEntityType.Builder.HasNoRelationship(fk, fromDataAnnotation: true) == null) { fk.Builder.HasNavigation( (string)null, existingNavigation.IsDependentToPrincipal(), fromDataAnnotation: true); } } var existingAmbiguousNavigation = FindActualEntityType(ambiguousInverse.Value.Item2) .FindNavigation(ambiguousInverse.Value.Item1); if (existingAmbiguousNavigation != null) { var fk = existingAmbiguousNavigation.ForeignKey; if (fk.IsOwnership || fk.DeclaringEntityType.Builder.HasNoRelationship(fk, fromDataAnnotation: true) == null) { fk.Builder.HasNavigation( (string)null, existingAmbiguousNavigation.IsDependentToPrincipal(), fromDataAnnotation: true); } } return(entityType.FindNavigation(navigationMemberInfo)?.ForeignKey.Builder); } var ownership = entityType.FindOwnership(); if (ownership != null && ownership.PrincipalEntityType == targetEntityTypeBuilder.Metadata && ownership.PrincipalToDependent?.GetIdentifyingMemberInfo() != inverseNavigationPropertyInfo) { Dependencies.Logger.NonOwnershipInverseNavigationWarning( entityType, navigationMemberInfo, targetEntityTypeBuilder.Metadata, inverseNavigationPropertyInfo, ownership.PrincipalToDependent?.GetIdentifyingMemberInfo()); return(null); } if (entityType.DefiningEntityType != null && entityType.DefiningEntityType == targetEntityTypeBuilder.Metadata && entityType.DefiningNavigationName != inverseNavigationPropertyInfo.GetSimpleMemberName()) { Dependencies.Logger.NonDefiningInverseNavigationWarning( entityType, navigationMemberInfo, targetEntityTypeBuilder.Metadata, inverseNavigationPropertyInfo, entityType.DefiningEntityType.GetRuntimeProperties()[entityType.DefiningNavigationName]); return(null); } return(entityType.Model.FindIsOwnedConfigurationSource(entityType.ClrType) != null && !entityType.IsInOwnershipPath(targetEntityTypeBuilder.Metadata) ? targetEntityTypeBuilder.HasOwnership( entityTypeBuilder.Metadata.ClrType, inverseNavigationPropertyInfo, navigationMemberInfo, fromDataAnnotation: true) : targetEntityTypeBuilder.HasRelationship( entityType, inverseNavigationPropertyInfo, navigationMemberInfo, fromDataAnnotation: true)); }
private void CreateRelationships( IEnumerable <RelationshipCandidate> relationshipCandidates, IConventionEntityTypeBuilder entityTypeBuilder) { var unusedEntityTypes = new List <IConventionEntityType>(); foreach (var relationshipCandidate in relationshipCandidates) { var entityType = entityTypeBuilder.Metadata; var targetEntityType = relationshipCandidate.TargetTypeBuilder.Metadata; var isAmbiguousOnBase = entityType.BaseType != null && HasAmbiguousNavigationsTo(entityType.BaseType, targetEntityType.ClrType) || (targetEntityType.BaseType != null && HasAmbiguousNavigationsTo(targetEntityType.BaseType, entityType.ClrType)); var ambiguousOwnership = relationshipCandidate.NavigationProperties.Count == 1 && relationshipCandidate.InverseProperties.Count == 1 && entityType.GetConfigurationSource() != ConfigurationSource.Explicit && targetEntityType.GetConfigurationSource() != ConfigurationSource.Explicit && targetEntityType.Model.IsOwned(entityType.ClrType) && targetEntityType.Model.IsOwned(targetEntityType.ClrType); if (ambiguousOwnership) { var existingNavigation = entityType.FindNavigation(relationshipCandidate.NavigationProperties.Single().GetSimpleMemberName()); if (existingNavigation != null && existingNavigation.ForeignKey.DeclaringEntityType == targetEntityType && existingNavigation.ForeignKey.GetPrincipalEndConfigurationSource() .OverridesStrictly(ConfigurationSource.Convention)) { ambiguousOwnership = false; } else { var existingInverse = targetEntityType.FindNavigation(relationshipCandidate.InverseProperties.Single().GetSimpleMemberName()); if (existingInverse != null && existingInverse.ForeignKey.PrincipalEntityType == targetEntityType && existingInverse.ForeignKey.GetPrincipalEndConfigurationSource() .OverridesStrictly(ConfigurationSource.Convention)) { ambiguousOwnership = false; } } } if ((relationshipCandidate.NavigationProperties.Count > 1 && relationshipCandidate.InverseProperties.Count > 0 && (!targetEntityType.Model.IsOwned(targetEntityType.ClrType) || entityType.IsInOwnershipPath(targetEntityType))) || relationshipCandidate.InverseProperties.Count > 1 || isAmbiguousOnBase || ambiguousOwnership || HasDeclaredAmbiguousNavigationsTo(entityType, targetEntityType.ClrType) || HasDeclaredAmbiguousNavigationsTo(targetEntityType, entityType.ClrType)) { if (!isAmbiguousOnBase) { Dependencies.Logger.MultipleNavigationProperties( relationshipCandidate.NavigationProperties.Count == 0 ? new[] { new Tuple <MemberInfo?, Type>(null, targetEntityType.ClrType) } : relationshipCandidate.NavigationProperties.Select( n => new Tuple <MemberInfo?, Type>(n, entityType.ClrType)), relationshipCandidate.InverseProperties.Count == 0 ? new[] { new Tuple <MemberInfo?, Type>(null, targetEntityType.ClrType) } : relationshipCandidate.InverseProperties.Select( n => new Tuple <MemberInfo?, Type>(n, targetEntityType.ClrType))); } foreach (var navigationProperty in relationshipCandidate.NavigationProperties.ToList()) { RemoveNavigation( navigationProperty, entityType, relationshipCandidate.NavigationProperties); } foreach (var inverseProperty in relationshipCandidate.InverseProperties.ToList()) { RemoveNavigation( inverseProperty, targetEntityType, relationshipCandidate.InverseProperties); } if (!isAmbiguousOnBase) { AddAmbiguous(entityTypeBuilder, relationshipCandidate.NavigationProperties, targetEntityType.ClrType); AddAmbiguous(targetEntityType.Builder, relationshipCandidate.InverseProperties, entityType.ClrType); } unusedEntityTypes.Add(targetEntityType); continue; } foreach (var navigation in relationshipCandidate.NavigationProperties) { if (!targetEntityType.IsInModel && !targetEntityType.Model.IsOwned(targetEntityType.ClrType)) { continue; } if (InversePropertyAttributeConvention.IsAmbiguous(entityType, navigation, targetEntityType)) { unusedEntityTypes.Add(targetEntityType); continue; } var targetOwned = !entityType.IsInOwnershipPath(targetEntityType) && (targetEntityType.Model.IsOwned(targetEntityType.ClrType) || (targetEntityType.HasSharedClrType && targetEntityType.Model.FindEntityTypes(targetEntityType.ClrType).Any(e => e.IsOwned()))); var inverse = relationshipCandidate.InverseProperties.SingleOrDefault(); if (inverse == null) { if (targetOwned) { entityTypeBuilder.HasOwnership(targetEntityType.ClrType, navigation); } else { entityTypeBuilder.HasRelationship(targetEntityType, navigation); } } else { if (InversePropertyAttributeConvention.IsAmbiguous(targetEntityType, inverse, entityType)) { unusedEntityTypes.Add(targetEntityType); continue; } if (targetOwned && entityType.Model.IsOwned(entityType.ClrType)) { var existingInverse = targetEntityType.FindNavigation(inverse.GetSimpleMemberName()); if (inverse.PropertyType.TryGetSequenceType() != null || targetEntityType.GetConfigurationSource() == ConfigurationSource.Explicit || (existingInverse != null && existingInverse.ForeignKey.DeclaringEntityType == entityType && existingInverse.ForeignKey.GetPrincipalEndConfigurationSource() .OverridesStrictly(ConfigurationSource.Convention))) { // Target type is the principal, so the ownership should be configured from the other side targetOwned = false; } } if (targetOwned) { entityTypeBuilder.HasOwnership(targetEntityType.ClrType, navigation, inverse); } else if (entityTypeBuilder.HasRelationship(targetEntityType, navigation, inverse) == null) { var navigationTargetType = navigation.PropertyType.TryGetSequenceType(); var inverseTargetType = inverse.PropertyType.TryGetSequenceType(); if (navigationTargetType == targetEntityType.ClrType && inverseTargetType == entityType.ClrType) { entityTypeBuilder.HasSkipNavigation( navigation, targetEntityType, inverse, collections: true, onDependent: false); } } } } if (relationshipCandidate.NavigationProperties.Count == 0) { if (relationshipCandidate.InverseProperties.Count == 0 || targetEntityType.Model.IsOwned(targetEntityType.ClrType)) { unusedEntityTypes.Add(targetEntityType); } else { foreach (var inverse in relationshipCandidate.InverseProperties) { if (!targetEntityType.IsInModel) { continue; } if (InversePropertyAttributeConvention.IsAmbiguous(targetEntityType, inverse, entityType)) { unusedEntityTypes.Add(targetEntityType); continue; } targetEntityType.Builder.HasRelationship(entityTypeBuilder.Metadata, inverse); } } } } foreach (var unusedEntityType in unusedEntityTypes) { if (IsImplicitlyCreatedUnusedSharedType(unusedEntityType)) { entityTypeBuilder.ModelBuilder.HasNoEntityType(unusedEntityType); } } }