示例#1
0
    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);
                }
            }
        }