diff --git a/src/EFCore/Extensions/ConventionEntityTypeExtensions.cs b/src/EFCore/Extensions/ConventionEntityTypeExtensions.cs index 329e22e352d..d2a9a9ff941 100644 --- a/src/EFCore/Extensions/ConventionEntityTypeExtensions.cs +++ b/src/EFCore/Extensions/ConventionEntityTypeExtensions.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; using System.Reflection; using JetBrains.Annotations; @@ -52,6 +53,30 @@ public static IEnumerable GetDerivedTypesInclusive([NotNu public static IEnumerable GetDirectlyDerivedTypes([NotNull] this IConventionEntityType entityType) => ((EntityType)entityType).GetDirectlyDerivedTypes(); + /// + /// Returns all base types of the given , including the type itself, top to bottom. + /// + /// The entity type. + /// Base types. + public static IEnumerable GetAllBaseTypesInclusive([NotNull] this IConventionEntityType entityType) + => GetAllBaseTypesInclusiveAscending(entityType).Reverse(); + + /// + /// Returns all base types of the given , including the type itself, bottom to top. + /// + /// The entity type. + /// Base types. + public static IEnumerable GetAllBaseTypesInclusiveAscending([NotNull] this IConventionEntityType entityType) + { + Check.NotNull(entityType, nameof(entityType)); + + while (entityType != null) + { + yield return entityType; + entityType = entityType.BaseType; + } + } + /// /// /// Gets all keys declared on the given . diff --git a/src/EFCore/Metadata/Builders/ReferenceNavigationBuilder.cs b/src/EFCore/Metadata/Builders/ReferenceNavigationBuilder.cs index f1a685323ca..8a154f5eaff 100644 --- a/src/EFCore/Metadata/Builders/ReferenceNavigationBuilder.cs +++ b/src/EFCore/Metadata/Builders/ReferenceNavigationBuilder.cs @@ -178,23 +178,13 @@ private InternalRelationshipBuilder WithManyBuilder(MemberIdentity collection) ThrowForConflictingNavigation(foreignKey, collectionName, false); } - return RelatedEntityType != foreignKey.PrincipalEntityType - ? collection.MemberInfo == null && ReferenceMember == null + return collection.MemberInfo == null || ReferenceMember == null ? builder.HasNavigations( ReferenceName, collection.Name, (EntityType)RelatedEntityType, (EntityType)DeclaringEntityType, ConfigurationSource.Explicit) : builder.HasNavigations( ReferenceMember, collection.MemberInfo, - (EntityType)RelatedEntityType, (EntityType)DeclaringEntityType, ConfigurationSource.Explicit) - : collection.MemberInfo != null - ? builder.HasNavigation( - collection.MemberInfo, - pointsToPrincipal: false, - ConfigurationSource.Explicit) - : builder.HasNavigation( - collection.Name, - pointsToPrincipal: false, - ConfigurationSource.Explicit); + (EntityType)RelatedEntityType, (EntityType)DeclaringEntityType, ConfigurationSource.Explicit); } /// diff --git a/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs b/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs index f4df70c8389..5e6b8d7e7c3 100644 --- a/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs +++ b/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs @@ -74,6 +74,7 @@ public virtual ConventionSet CreateConventionSet() conventionSet.EntityTypeAddedConventions.Add(new DerivedTypeDiscoveryConvention(Dependencies)); conventionSet.EntityTypeIgnoredConventions.Add(inversePropertyAttributeConvention); + conventionSet.EntityTypeIgnoredConventions.Add(relationshipDiscoveryConvention); var discriminatorConvention = new DiscriminatorConvention(Dependencies); conventionSet.EntityTypeRemovedConventions.Add(new OwnedTypesConvention(Dependencies)); diff --git a/src/EFCore/Metadata/Conventions/RelationshipDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/RelationshipDiscoveryConvention.cs index ee94edb3a1d..b24f00141ae 100644 --- a/src/EFCore/Metadata/Conventions/RelationshipDiscoveryConvention.cs +++ b/src/EFCore/Metadata/Conventions/RelationshipDiscoveryConvention.cs @@ -21,6 +21,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions /// public class RelationshipDiscoveryConvention : IEntityTypeAddedConvention, + IEntityTypeIgnoredConvention, IEntityTypeBaseTypeChangedConvention, INavigationRemovedConvention, IEntityTypeMemberIgnoredConvention, @@ -770,15 +771,24 @@ public virtual void ProcessEntityTypeBaseTypeChanged( DiscoverRelationships(oldBaseTypeBuilder, context); } - if (entityTypeBuilder.Metadata.BaseType != newBaseType) + var entityType = entityTypeBuilder.Metadata; + if (entityType.BaseType != newBaseType) { return; } - ApplyOnRelatedEntityTypes(entityTypeBuilder.Metadata, context); - foreach (var entityType in entityTypeBuilder.Metadata.GetDerivedTypesInclusive()) + if (newBaseType != null) { - DiscoverRelationships(entityType.Builder, context); + foreach (var ignoredMember in newBaseType.GetAllBaseTypesInclusive().SelectMany(et => et.GetIgnoredMembers())) + { + ProcessEntityTypeMemberIgnoredOnBase(entityType, ignoredMember); + } + } + + ApplyOnRelatedEntityTypes(entityType, context); + foreach (var derivedEntityType in entityType.GetDerivedTypesInclusive()) + { + DiscoverRelationships(derivedEntityType.Builder, context); } } @@ -849,6 +859,28 @@ private static bool IsCandidateNavigationProperty( && (!sourceEntityTypeBuilder.Metadata.IsKeyless || (memberInfo as PropertyInfo)?.PropertyType.TryGetSequenceType() == null); + /// + /// Called after an entity type is ignored. + /// + /// The builder for the model. + /// The name of the ignored entity type. + /// The ignored entity type. + /// Additional information associated with convention execution. + public virtual void ProcessEntityTypeIgnored( + IConventionModelBuilder modelBuilder, string name, Type type, IConventionContext context) + { + foreach (var entityType in modelBuilder.Metadata.GetEntityTypes()) + { + // Only run the convention if an ambiguity might have been removed + var ambiguityRemoved = RemoveAmbiguous(entityType, type); + + if (ambiguityRemoved) + { + DiscoverRelationships(entityType.Builder, context); + } + } + } + /// /// Called after an entity type member is ignored. /// @@ -861,43 +893,48 @@ public virtual void ProcessEntityTypeMemberIgnored( var anyAmbiguityRemoved = false; foreach (var derivedEntityType in entityTypeBuilder.Metadata.GetDerivedTypesInclusive()) { - var ambiguousNavigations = GetAmbiguousNavigations(derivedEntityType); - if (ambiguousNavigations == null) - { - continue; - } - - KeyValuePair? ambiguousNavigation = null; - foreach (var navigation in ambiguousNavigations) - { - if (navigation.Key.GetSimpleMemberName() == name) - { - ambiguousNavigation = navigation; - } - } - - if (ambiguousNavigation == null) - { - continue; - } + anyAmbiguityRemoved |= ProcessEntityTypeMemberIgnoredOnBase(derivedEntityType, name); + } - anyAmbiguityRemoved = true; + if (anyAmbiguityRemoved) + { + DiscoverRelationships(entityTypeBuilder, context); + } + } - var targetClrType = ambiguousNavigation.Value.Value; - RemoveAmbiguous(derivedEntityType, targetClrType); + private bool ProcessEntityTypeMemberIgnoredOnBase(IConventionEntityType entityType, string name) + { + var ambiguousNavigations = GetAmbiguousNavigations(entityType); + if (ambiguousNavigations == null) + { + return false; + } - var targetType = ((InternalEntityTypeBuilder)entityTypeBuilder) - .GetTargetEntityTypeBuilder(targetClrType, ambiguousNavigation.Value.Key, null)?.Metadata; - if (targetType != null) + KeyValuePair? ambiguousNavigation = null; + foreach (var navigation in ambiguousNavigations) + { + if (navigation.Key.GetSimpleMemberName() == name) { - RemoveAmbiguous(targetType, derivedEntityType.ClrType); + ambiguousNavigation = navigation; } } - if (anyAmbiguityRemoved) + if (ambiguousNavigation == null) { - DiscoverRelationships(entityTypeBuilder, context); + return false; } + + var targetClrType = ambiguousNavigation.Value.Value; + RemoveAmbiguous(entityType, targetClrType); + + var targetType = ((InternalEntityTypeBuilder)entityType.Builder) + .GetTargetEntityTypeBuilder(targetClrType, ambiguousNavigation.Value.Key, null)?.Metadata; + if (targetType != null) + { + RemoveAmbiguous(targetType, entityType.ClrType); + } + + return true; } /// diff --git a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs index 55a856451d1..88e72d8cbcf 100644 --- a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs @@ -445,7 +445,7 @@ private InternalPropertyBuilder Property( ConfigurationSource? typeConfigurationSource, ConfigurationSource? configurationSource) { - IEnumerable propertiesToDetach = null; + List propertiesToDetach = null; var existingProperty = Metadata.FindProperty(propertyName); if (existingProperty != null) { @@ -456,7 +456,7 @@ private InternalPropertyBuilder Property( { if (configurationSource.Overrides(existingProperty.GetConfigurationSource())) { - propertiesToDetach = new[] + propertiesToDetach = new List { existingProperty }; @@ -527,7 +527,19 @@ private InternalPropertyBuilder Property( Metadata.RemoveIgnored(propertyName); - propertiesToDetach = Metadata.FindDerivedProperties(propertyName); + foreach (var derivedType in Metadata.GetDerivedTypes()) + { + var derivedProperty = derivedType.FindDeclaredProperty(propertyName); + if (derivedProperty != null) + { + if (propertiesToDetach == null) + { + propertiesToDetach = new List(); + } + + propertiesToDetach.Add(derivedProperty); + } + } } InternalPropertyBuilder builder; diff --git a/src/EFCore/Metadata/Internal/InternalRelationshipBuilder.cs b/src/EFCore/Metadata/Internal/InternalRelationshipBuilder.cs index 84aa352ae82..f8d1511b2e0 100644 --- a/src/EFCore/Metadata/Internal/InternalRelationshipBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalRelationshipBuilder.cs @@ -234,7 +234,8 @@ private InternalRelationshipBuilder HasNavigations( out var shouldInvert, out var shouldBeUnique, out var removeOppositeNavigation, - out var removeConflictingNavigations)) + out var removeConflictingNavigations, + out var changeRelatedTypes)) { return null; } @@ -280,7 +281,8 @@ private InternalRelationshipBuilder HasNavigations( InternalRelationshipBuilder builder; if (shouldInvert == true - || removeConflictingNavigations) + || removeConflictingNavigations + || changeRelatedTypes) { builder = ReplaceForeignKey( configurationSource, @@ -291,7 +293,7 @@ private InternalRelationshipBuilder HasNavigations( dependentProperties, principalProperties: principalProperties, isUnique: shouldBeUnique, - removeCurrent: shouldInvert ?? false, + removeCurrent: shouldInvert == true || changeRelatedTypes, principalEndConfigurationSource: shouldInvert != null ? configurationSource : (ConfigurationSource?)null, oldRelationshipInverted: shouldInvert == true); @@ -546,7 +548,8 @@ private bool CanSetNavigations( out shouldInvert, out shouldBeUnique, out removeOppositeNavigation, - out removeConflictingNavigations); + out removeConflictingNavigations, + out _); private bool CanSetNavigations( MemberIdentity? navigationToPrincipal, @@ -559,12 +562,14 @@ private bool CanSetNavigations( out bool? shouldInvert, out bool? shouldBeUnique, out bool removeOppositeNavigation, - out bool removeConflictingNavigations) + out bool removeConflictingNavigations, + out bool changeRelatedTypes) { shouldInvert = null; shouldBeUnique = null; removeOppositeNavigation = false; removeConflictingNavigations = false; + changeRelatedTypes = false; if ((navigationToPrincipal == null || navigationToPrincipal.Value.Name == Metadata.DependentToPrincipal?.Name) @@ -736,6 +741,38 @@ private bool CanSetNavigations( dependentProperties: null, principalProperties: null).Where(r => r.Metadata != Metadata).Distinct().Any(); + if (shouldInvert == false + && !removeConflictingNavigations + && (principalEntityType != Metadata.PrincipalEntityType + || dependentEntityType != Metadata.DeclaringEntityType)) + { + if (navigationToPrincipalProperty != null + && !IsCompatible( + navigationToPrincipalProperty, + pointsToPrincipal: true, + Metadata.DeclaringEntityType.ClrType, + Metadata.PrincipalEntityType.ClrType, + shouldThrow: false, + out _)) + { + changeRelatedTypes = true; + return true; + } + + if (navigationToDependentProperty != null + && !IsCompatible( + navigationToDependentProperty, + pointsToPrincipal: false, + Metadata.DeclaringEntityType.ClrType, + Metadata.PrincipalEntityType.ClrType, + shouldThrow: false, + out _)) + { + changeRelatedTypes = true; + return true; + } + } + return true; } @@ -775,7 +812,7 @@ private static bool IsCompatible( navigationProperty, principalType, dependentType, - shouldBeCollection: false, + shouldBeCollection: null, shouldThrow: true); } diff --git a/src/EFCore/Metadata/Internal/Navigation.cs b/src/EFCore/Metadata/Internal/Navigation.cs index f16b18f181e..6f2a753509a 100644 --- a/src/EFCore/Metadata/Internal/Navigation.cs +++ b/src/EFCore/Metadata/Internal/Navigation.cs @@ -160,38 +160,37 @@ public static bool IsCompatible( } var navigationTargetClrType = navigationProperty.GetMemberType().TryGetSequenceType(); - if (shouldBeCollection == false - || navigationTargetClrType?.GetTypeInfo().IsAssignableFrom(targetClrType.GetTypeInfo()) != true) + shouldBeCollection ??= navigationTargetClrType != null && navigationProperty.GetMemberType() != targetClrType; + if (shouldBeCollection.Value + && navigationTargetClrType?.GetTypeInfo().IsAssignableFrom(targetClrType.GetTypeInfo()) != true) { - if (shouldBeCollection == true) + if (shouldThrow) { - if (shouldThrow) - { - throw new InvalidOperationException( - CoreStrings.NavigationCollectionWrongClrType( - navigationProperty.Name, - sourceClrType.ShortDisplayName(), - navigationProperty.GetMemberType().ShortDisplayName(), - targetClrType.ShortDisplayName())); - } - - return false; + throw new InvalidOperationException( + CoreStrings.NavigationCollectionWrongClrType( + navigationProperty.Name, + sourceClrType.ShortDisplayName(), + navigationProperty.GetMemberType().ShortDisplayName(), + targetClrType.ShortDisplayName())); } - if (!navigationProperty.GetMemberType().GetTypeInfo().IsAssignableFrom(targetClrType.GetTypeInfo())) + return false; + } + + if (!shouldBeCollection.Value + && !navigationProperty.GetMemberType().GetTypeInfo().IsAssignableFrom(targetClrType.GetTypeInfo())) + { + if (shouldThrow) { - if (shouldThrow) - { - throw new InvalidOperationException( - CoreStrings.NavigationSingleWrongClrType( - navigationProperty.Name, - sourceClrType.ShortDisplayName(), - navigationProperty.GetMemberType().ShortDisplayName(), - targetClrType.ShortDisplayName())); - } - - return false; + throw new InvalidOperationException( + CoreStrings.NavigationSingleWrongClrType( + navigationProperty.Name, + sourceClrType.ShortDisplayName(), + navigationProperty.GetMemberType().ShortDisplayName(), + targetClrType.ShortDisplayName())); } + + return false; } return true; diff --git a/src/EFCore/Metadata/Internal/Property.cs b/src/EFCore/Metadata/Internal/Property.cs index 7d029b737ba..2fad2f0a679 100644 --- a/src/EFCore/Metadata/Internal/Property.cs +++ b/src/EFCore/Metadata/Internal/Property.cs @@ -117,8 +117,12 @@ public virtual InternalPropertyBuilder Builder /// 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. /// - public virtual void UpdateConfigurationSource(ConfigurationSource configurationSource) - => _configurationSource = _configurationSource.Max(configurationSource); + public virtual bool UpdateConfigurationSource(ConfigurationSource configurationSource) + { + var oldConfigurationSource = _configurationSource; + _configurationSource = _configurationSource.Max(configurationSource); + return _configurationSource != oldConfigurationSource; + } // Needed for a workaround before reference counting is implemented // Issue #214 diff --git a/test/EFCore.Tests/Metadata/Internal/InternalEntityTypeBuilderTest.cs b/test/EFCore.Tests/Metadata/Internal/InternalEntityTypeBuilderTest.cs index 50824071663..fe3bd82f0de 100644 --- a/test/EFCore.Tests/Metadata/Internal/InternalEntityTypeBuilderTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/InternalEntityTypeBuilderTest.cs @@ -1689,14 +1689,12 @@ private void VerifyIgnoreProperty( ConfigurationSource addConfigurationSource, bool ignoredFirst, bool setBaseFirst) - { - VerifyIgnoreMember( + => VerifyIgnoreMember( ignoredOnType, ignoreConfigurationSource, addConfigurationSource, ignoredFirst, setBaseFirst, et => et.Metadata.FindProperty(OrderMinimal.CustomerIdProperty.Name) != null, et => et.Property(OrderMinimal.CustomerIdProperty, addConfigurationSource) != null, et => et.Property(OrderMinimal.CustomerIdProperty, ignoreConfigurationSource) != null, OrderMinimal.CustomerIdProperty.Name); - } private void VerifyIgnoreMember( Type ignoredOnType, @@ -1784,11 +1782,11 @@ private void VerifyIgnoreMember( Assert.True(unignoreMember(ignoredEntityTypeBuilder)); } + Assert.Equal(expectedAdded, findMember(addedEntityTypeBuilder)); Assert.Equal( expectedIgnored, ignoredEntityTypeBuilder.Metadata.FindDeclaredIgnoredConfigurationSource(memberToIgnore) == ignoreConfigurationSource); - Assert.Equal(expectedAdded, findMember(addedEntityTypeBuilder)); } private void ConfigureOrdersHierarchy(InternalModelBuilder modelBuilder) diff --git a/test/EFCore.Tests/ModelBuilding/InheritanceTestBase.cs b/test/EFCore.Tests/ModelBuilding/InheritanceTestBase.cs index eeac911062b..95cfde4bb2d 100644 --- a/test/EFCore.Tests/ModelBuilding/InheritanceTestBase.cs +++ b/test/EFCore.Tests/ModelBuilding/InheritanceTestBase.cs @@ -174,6 +174,8 @@ public virtual void Setting_base_type_to_null_fixes_relationships() derivedDependentEntityBuilder.HasBaseType(null); + modelBuilder.FinalizeModel(); + fk = dependentEntityBuilder.Metadata.GetNavigations().Single().ForeignKey; Assert.Equal(nameof(Order.Customer), fk.DependentToPrincipal.Name); Assert.Null(fk.PrincipalToDependent); @@ -226,6 +228,8 @@ public virtual void Pulling_relationship_to_a_derived_type_creates_relationships .HasMany(e => e.SpecialOrders) .WithOne(e => (SpecialCustomer)e.Customer); + modelBuilder.FinalizeModel(); + Assert.Empty(dependentEntityBuilder.Metadata.GetForeignKeys()); Assert.Empty(dependentEntityBuilder.Metadata.GetNavigations()); var newFk = derivedDependentEntityBuilder.Metadata.GetDeclaredNavigations().Single().ForeignKey; @@ -260,6 +264,8 @@ public virtual void Pulling_relationship_to_a_derived_type_reverted_creates_rela .HasOne(e => (SpecialCustomer)e.Customer) .WithMany(e => e.SpecialOrders); + modelBuilder.FinalizeModel(); + Assert.Empty(dependentEntityBuilder.Metadata.GetForeignKeys()); Assert.Empty(dependentEntityBuilder.Metadata.GetNavigations()); var newFk = derivedDependentEntityBuilder.Metadata.GetDeclaredNavigations().Single().ForeignKey; @@ -272,6 +278,35 @@ public virtual void Pulling_relationship_to_a_derived_type_reverted_creates_rela Assert.Equal(nameof(Order.CustomerId), otherDerivedFk.Properties.Single().Name); } + [ConditionalFact] + public virtual void Can_match_navigation_to_derived_type_with_inverse_on_base() + { + var modelBuilder = CreateModelBuilder(); + modelBuilder.Ignore(); + modelBuilder.Ignore(); + + var principalEntityBuilder = modelBuilder.Entity(); + principalEntityBuilder.Ignore(nameof(Customer.Orders)); + var dependentEntityBuilder = modelBuilder.Entity(); + var derivedDependentEntityBuilder = modelBuilder.Entity(); + derivedDependentEntityBuilder.Ignore(nameof(SpecialOrder.BackOrder)); + derivedDependentEntityBuilder.Ignore(nameof(SpecialOrder.SpecialCustomer)); + derivedDependentEntityBuilder.Ignore(nameof(SpecialOrder.ShippingAddress)); + + derivedDependentEntityBuilder + .HasOne(e => e.Customer) + .WithMany(e => e.SomeOrders); + + modelBuilder.FinalizeModel(); + + Assert.Empty(dependentEntityBuilder.Metadata.GetForeignKeys()); + Assert.Empty(dependentEntityBuilder.Metadata.GetNavigations()); + var newFk = derivedDependentEntityBuilder.Metadata.GetDeclaredNavigations().Single().ForeignKey; + Assert.Equal(nameof(Order.Customer), newFk.DependentToPrincipal.Name); + Assert.Equal(nameof(Customer.SomeOrders), newFk.PrincipalToDependent.Name); + Assert.Same(principalEntityBuilder.Metadata, newFk.PrincipalEntityType); + } + [ConditionalFact] public virtual void Pulling_relationship_to_a_derived_type_many_to_one_creates_relationships_on_other_derived_types() { @@ -402,7 +437,6 @@ public virtual void Can_promote_shadow_fk_to_the_base_type() var dependentEntityBuilder = modelBuilder.Entity(); dependentEntityBuilder.Ignore(e => e.Customer); var derivedDependentEntityBuilder = modelBuilder.Entity(); - derivedDependentEntityBuilder.Ignore(e => e.SpecialCustomerId); derivedDependentEntityBuilder.Ignore(e => e.ShippingAddress); dependentEntityBuilder @@ -410,6 +444,8 @@ public virtual void Can_promote_shadow_fk_to_the_base_type() .WithMany() .HasForeignKey(nameof(SpecialOrder.SpecialCustomerId)); + modelBuilder.FinalizeModel(); + var newFk = dependentEntityBuilder.Metadata.GetDeclaredForeignKeys().Single(); Assert.NotEqual(newFk, derivedDependentEntityBuilder.Metadata.GetDeclaredForeignKeys().Single()); Assert.Null(newFk.DependentToPrincipal); diff --git a/test/EFCore.Tests/ModelBuilding/ModelBuilder.Other.cs b/test/EFCore.Tests/ModelBuilding/ModelBuilder.Other.cs index 9e759ab9e51..be41422a9e8 100644 --- a/test/EFCore.Tests/ModelBuilding/ModelBuilder.Other.cs +++ b/test/EFCore.Tests/ModelBuilding/ModelBuilder.Other.cs @@ -65,6 +65,22 @@ public virtual void HasMany_with_a_non_collection_just_string_navigation_CLR_pro } } + [ConditionalFact] + public virtual void HasMany_with_a_collection_navigation_CLR_property_to_derived_type_throws() + { + using (var context = new CustomModelBuildingContext( + Configure(), + b => + { + b.Entity().HasMany(d => d.Jrs); + })) + { + Assert.Equal( + CoreStrings.NavigationCollectionWrongClrType(nameof(Dr.Jrs), nameof(Dr), "ICollection", nameof(Dre)), + Assert.Throws(() => context.Model).Message); + } + } + [ConditionalFact] public virtual void OwnsOne_HasOne_with_just_string_navigation_for_non_CLR_property_throws() { @@ -72,7 +88,7 @@ public virtual void OwnsOne_HasOne_with_just_string_navigation_for_non_CLR_prope Configure(), b => { - b.Entity().OwnsOne(e =>e.Dre).HasOne("Snoop"); + b.Entity().OwnsOne(e => e.Dre).HasOne("Snoop"); })) { Assert.Equal( @@ -86,12 +102,18 @@ protected class Dr public int Id { get; set; } public Dre Dre { get; set; } + + public ICollection Jrs { get; set; } } protected class Dre { } + protected class DreJr : Dre + { + } + [ConditionalFact] //Issue#13108 public virtual void HasForeignKey_infers_type_for_shadow_property_when_not_specified() { diff --git a/test/EFCore.Tests/ModelBuilding/TestModel.cs b/test/EFCore.Tests/ModelBuilding/TestModel.cs index c5d6c2d4d18..cd6b9fb87fa 100644 --- a/test/EFCore.Tests/ModelBuilding/TestModel.cs +++ b/test/EFCore.Tests/ModelBuilding/TestModel.cs @@ -94,6 +94,9 @@ protected class Customer public IEnumerable Orders { get; set; } + [NotMapped] + public ICollection SomeOrders { get; set; } + public CustomerDetails Details { get; set; } }