From 29f2691f78acd1c16b6cf28b2bea5f5ec03b6293 Mon Sep 17 00:00:00 2001 From: Andriy Svyryd Date: Thu, 20 Aug 2020 12:50:02 -0700 Subject: [PATCH] Re-run fk discovery convention when skip navigation changes Unify base FK property name logic Unify implicit property logic Fixes #22128 --- src/EFCore/Extensions/PropertyExtensions.cs | 5 ++ src/EFCore/Infrastructure/ModelValidator.cs | 18 +++-- .../ForeignKeyPropertyDiscoveryConvention.cs | 70 +++++++++++++++---- .../ProviderConventionSetBuilder.cs | 2 + .../Conventions/KeyDiscoveryConvention.cs | 2 +- src/EFCore/Metadata/IConventionProperty.cs | 8 +++ .../Internal/InternalEntityTypeBuilder.cs | 31 +++++--- .../Internal/InternalForeignKeyBuilder.cs | 10 +-- .../Internal/CosmosDatabaseCreatorTest.cs | 4 +- .../ChangeTracking/SkipCollectionEntryTest.cs | 4 +- .../Infrastructure/ModelValidatorTest.cs | 4 +- .../Internal/InternalEntityTypeBuilderTest.cs | 4 +- .../ModelBuilding/ManyToManyTestBase.cs | 14 +++- 13 files changed, 132 insertions(+), 44 deletions(-) diff --git a/src/EFCore/Extensions/PropertyExtensions.cs b/src/EFCore/Extensions/PropertyExtensions.cs index 2111bf748a8..60167fdabce 100644 --- a/src/EFCore/Extensions/PropertyExtensions.cs +++ b/src/EFCore/Extensions/PropertyExtensions.cs @@ -446,6 +446,11 @@ public static string ToDebugString( builder.Append(" Shadow"); } + if (property.IsIndexerProperty()) + { + builder.Append(" Indexer"); + } + if (!property.IsNullable) { builder.Append(" Required"); diff --git a/src/EFCore/Infrastructure/ModelValidator.cs b/src/EFCore/Infrastructure/ModelValidator.cs index 4175ae52c7f..4d7bb871083 100644 --- a/src/EFCore/Infrastructure/ModelValidator.cs +++ b/src/EFCore/Infrastructure/ModelValidator.cs @@ -157,7 +157,7 @@ protected virtual void ValidatePropertyMapping( var unmappedProperty = entityType.GetDeclaredProperties().FirstOrDefault( p => (!ConfigurationSource.Convention.Overrides(p.GetConfigurationSource()) // Use a better condition of non-persisted properties when issue#14121 is implemented - || !(p.IsShadowProperty() || (p.DeclaringEntityType.IsPropertyBag && p.IsIndexerProperty()))) + || !p.IsImplicitlyCreated()) && p.FindTypeMapping() == null); if (unmappedProperty != null) @@ -398,13 +398,12 @@ protected virtual void ValidateNoShadowKeys( { Check.NotNull(model, nameof(model)); - foreach (var entityType in model.GetEntityTypes().Where(t => t.ClrType != null)) + foreach (IConventionEntityType entityType in model.GetEntityTypes().Where(t => t.ClrType != null)) { foreach (var key in entityType.GetDeclaredKeys()) { - if (key.Properties.Any(p => p.IsShadowProperty()) - && key is Key concreteKey - && ConfigurationSource.Convention.Overrides(concreteKey.GetConfigurationSource()) + if (key.Properties.Any(p => p.IsImplicitlyCreated()) + && ConfigurationSource.Convention.Overrides(key.GetConfigurationSource()) && !key.IsPrimaryKey()) { var referencingFk = key.GetReferencingForeignKeys().FirstOrDefault(); @@ -1162,11 +1161,16 @@ protected virtual void LogShadowProperties( { Check.NotNull(model, nameof(model)); - foreach (var entityType in model.GetEntityTypes().Where(t => t.ClrType != null)) + foreach (IConventionEntityType entityType in model.GetEntityTypes()) { + if (entityType.ClrType == null) + { + continue; + } + foreach (var property in entityType.GetDeclaredProperties()) { - if (property.IsShadowProperty()) + if (property.IsImplicitlyCreated()) { logger.ShadowPropertyCreated(property); } diff --git a/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs index 85b22dbd282..f3780c4ec27 100644 --- a/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs +++ b/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -48,6 +48,8 @@ public class ForeignKeyPropertyDiscoveryConvention : IForeignKeyPrincipalEndChangedConvention, IForeignKeyUniquenessChangedConvention, IForeignKeyRequirednessChangedConvention, + ISkipNavigationForeignKeyChangedConvention, + ISkipNavigationInverseChangedConvention, IKeyAddedConvention, IKeyRemovedConvention, IEntityTypePrimaryKeyChangedConvention, @@ -135,7 +137,7 @@ private IConventionForeignKeyBuilder DiscoverProperties( foreach (var fkProperty in foreignKey.Properties) { if (ConfigurationSource.Convention.Overrides(fkProperty.GetTypeConfigurationSource()) - && fkProperty.IsShadowProperty() + && (fkProperty.IsShadowProperty() || fkProperty.IsIndexerProperty()) && fkProperty.ClrType.IsNullableType() == foreignKey.IsRequired && fkProperty.GetContainingForeignKeys().All(otherFk => otherFk.IsRequired == foreignKey.IsRequired)) { @@ -279,7 +281,7 @@ private IConventionForeignKeyBuilder DiscoverProperties( if (foreignKeyProperties == null) { - return ((ForeignKey)foreignKey).Builder.ReuniquifyTemporaryProperties(false); + return ((ForeignKey)foreignKey).Builder.ReuniquifyImplicitProperties(false); } var conflictingFKCount = foreignKey.DeclaringEntityType.FindForeignKeys(foreignKeyProperties) @@ -290,13 +292,13 @@ private IConventionForeignKeyBuilder DiscoverProperties( if (foreignKey.Properties.SequenceEqual(foreignKeyProperties)) { return conflictingFKCount > 1 - ? ((ForeignKey)foreignKey).Builder.ReuniquifyTemporaryProperties(true) + ? ((ForeignKey)foreignKey).Builder.ReuniquifyImplicitProperties(true) : relationshipBuilder; } if (conflictingFKCount > 0) { - return ((ForeignKey)foreignKey).Builder.ReuniquifyTemporaryProperties(false); + return ((ForeignKey)foreignKey).Builder.ReuniquifyImplicitProperties(false); } var newRelationshipBuilder = relationshipBuilder.HasForeignKey(foreignKeyProperties); @@ -422,7 +424,7 @@ private bool TryFindMatchingProperties( shouldThrow: false)) { if (propertiesToReference.All( - p => !p.IsShadowProperty() + p => !p.IsImplicitlyCreated() || p.GetConfigurationSource().Overrides(ConfigurationSource.DataAnnotation))) { var dependentNavigationSpec = onDependent @@ -482,7 +484,7 @@ private static IConventionProperty TryGetProperty(IConventionEntityType entityTy { foreach (var property in entityType.GetProperties()) { - if ((!(property.IsShadowProperty() || entityType.IsPropertyBag && property.IsIndexerProperty()) + if ((!property.IsImplicitlyCreated() || !ConfigurationSource.Convention.Overrides(property.GetConfigurationSource())) && property.Name.Length == prefix.Length + suffix.Length && property.Name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) @@ -524,7 +526,7 @@ public virtual void ProcessPropertyAdded( private void Process(IConventionPropertyBuilder propertyBuilder, IConventionContext context) { var property = propertyBuilder.Metadata; - if (property.IsShadowProperty() + if (property.IsImplicitlyCreated() && ConfigurationSource.Convention.Overrides(property.GetConfigurationSource())) { return; @@ -756,6 +758,37 @@ public virtual void ProcessEntityTypePrimaryKeyChanged( } } + /// + public virtual void ProcessSkipNavigationForeignKeyChanged( + IConventionSkipNavigationBuilder skipNavigationBuilder, + IConventionForeignKey foreignKey, + IConventionForeignKey oldForeignKey, + IConventionContext context) + { + if (foreignKey != null + && foreignKey.GetPropertiesConfigurationSource() == null + && skipNavigationBuilder.Metadata.Inverse?.Builder != null) + { + DiscoverProperties(foreignKey.Builder, context); + } + } + + /// + public virtual void ProcessSkipNavigationInverseChanged( + IConventionSkipNavigationBuilder skipNavigationBuilder, + IConventionSkipNavigation inverse, + IConventionSkipNavigation oldInverse, + IConventionContext context) + { + var foreignKey = skipNavigationBuilder.Metadata.ForeignKey; + if (foreignKey != null + && foreignKey.GetPropertiesConfigurationSource() == null + && inverse?.Builder != null) + { + DiscoverProperties(foreignKey.Builder, context); + } + } + /// public virtual void ProcessModelFinalizing( IConventionModelBuilder modelBuilder, @@ -802,33 +835,44 @@ public virtual void ProcessModelFinalizing( if (HasUniquifiedProperties(foreignKey)) { - var conflictingShadowFk = entityType.GetDeclaredForeignKeys().FirstOrDefault( + var conflictingFk = entityType.GetDeclaredForeignKeys().FirstOrDefault( otherForeignKey => otherForeignKey != foreignKey && otherForeignKey.PrincipalEntityType == foreignKey.PrincipalEntityType && otherForeignKey.GetPropertiesConfigurationSource() == null); - if (conflictingShadowFk != null) + if (conflictingFk != null) { conflictingFkFound = true; - Dependencies.Logger.ConflictingShadowForeignKeysWarning(conflictingShadowFk); + Dependencies.Logger.ConflictingShadowForeignKeysWarning(conflictingFk); } } } } } - private static string GetPropertyBaseName(IConventionForeignKey foreignKey) + /// + /// Gets the string that should be used as part of the shadow properties created for the given foreign key. + /// + /// The foreign key. + /// The string that should be used as part of the shadow properties created for the given foreign key. + public static string GetPropertyBaseName([NotNull] IForeignKey foreignKey) => foreignKey.DependentToPrincipal?.Name ?? foreignKey.GetReferencingSkipNavigations().FirstOrDefault()?.Inverse?.Name ?? foreignKey.PrincipalEntityType.ShortName(); private static bool HasUniquifiedProperties(IConventionForeignKey foreignKey) { + if (foreignKey.GetPropertiesConfigurationSource() != null) + { + return false; + } + var fkBaseName = GetPropertyBaseName(foreignKey); for (var i = 0; i < foreignKey.Properties.Count; i++) { var property = foreignKey.Properties[i]; - if (!ConfigurationSource.Convention.Overrides(property.GetConfigurationSource())) + if (!ConfigurationSource.Convention.Overrides(property.GetConfigurationSource()) + || !property.IsImplicitlyCreated()) { return false; } diff --git a/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs b/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs index 3a2500ae3a2..b0c83a49e3a 100644 --- a/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs +++ b/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs @@ -187,9 +187,11 @@ public virtual ConventionSet CreateConventionSet() conventionSet.SkipNavigationRemovedConventions.Add(manyToManyJoinEntityTypeConvention); conventionSet.SkipNavigationInverseChangedConventions.Add(manyToManyJoinEntityTypeConvention); + conventionSet.SkipNavigationInverseChangedConventions.Add(foreignKeyPropertyDiscoveryConvention); conventionSet.SkipNavigationForeignKeyChangedConventions.Add(manyToManyJoinEntityTypeConvention); conventionSet.SkipNavigationForeignKeyChangedConventions.Add(keyDiscoveryConvention); + conventionSet.SkipNavigationForeignKeyChangedConventions.Add(foreignKeyPropertyDiscoveryConvention); conventionSet.NavigationRemovedConventions.Add(relationshipDiscoveryConvention); diff --git a/src/EFCore/Metadata/Conventions/KeyDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/KeyDiscoveryConvention.cs index 81cb0f61e2c..5436aa5b55b 100644 --- a/src/EFCore/Metadata/Conventions/KeyDiscoveryConvention.cs +++ b/src/EFCore/Metadata/Conventions/KeyDiscoveryConvention.cs @@ -86,7 +86,7 @@ protected virtual void TryConfigurePrimaryKey([NotNull] IConventionEntityTypeBui if (keyProperties == null) { var candidateProperties = entityType.GetProperties().Where( - p => !p.IsShadowProperty() + p => !p.IsImplicitlyCreated() || !ConfigurationSource.Convention.Overrides(p.GetConfigurationSource())); keyProperties = DiscoverKeyProperties(entityType, candidateProperties).ToList(); if (keyProperties.Count > 1) diff --git a/src/EFCore/Metadata/IConventionProperty.cs b/src/EFCore/Metadata/IConventionProperty.cs index 6bed6ad3f60..641fde0ef87 100644 --- a/src/EFCore/Metadata/IConventionProperty.cs +++ b/src/EFCore/Metadata/IConventionProperty.cs @@ -90,5 +90,13 @@ public interface IConventionProperty : IProperty, IConventionPropertyBase /// /// The configuration source for . ConfigurationSource? GetIsConcurrencyTokenConfigurationSource(); + + /// + /// Returns a value indicating whether the property was created implicitly and isn't based on the CLR model. + /// + /// A value indicating whether the property was created implicitly and isn't based on the CLR model. + bool IsImplicitlyCreated() + => (this.IsShadowProperty() || (DeclaringEntityType.IsPropertyBag && this.IsIndexerProperty())) + && GetConfigurationSource() == ConfigurationSource.Convention; } } diff --git a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs index e0b2caf2228..49c0b7a2b8f 100644 --- a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs @@ -12,6 +12,7 @@ using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.EntityFrameworkCore.Metadata.Conventions; using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal; using Microsoft.EntityFrameworkCore.Utilities; using Microsoft.EntityFrameworkCore.ValueGeneration.Internal; @@ -445,8 +446,8 @@ public virtual InternalPropertyBuilder Property( ConfigurationSource? configurationSource) => Property( propertyType, propertyName, memberInfo: null, - typeConfigurationSource: typeConfigurationSource, - configurationSource: configurationSource); + typeConfigurationSource, + configurationSource); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -527,7 +528,18 @@ private InternalPropertyBuilder Property( return existingProperty.Builder; } - if (!configurationSource.Overrides(existingProperty.GetConfigurationSource())) + if (memberInfo == null + || (memberInfo is PropertyInfo propertyInfo && propertyInfo.IsIndexerProperty())) + { + if (existingProperty.GetTypeConfigurationSource() is ConfigurationSource existingTypeConfigurationSource + && !typeConfigurationSource.Overrides(existingTypeConfigurationSource)) + { + return null; + } + + memberInfo ??= existingProperty.GetIdentifyingMemberInfo(); + } + else if (!configurationSource.Overrides(existingProperty.GetConfigurationSource())) { return null; } @@ -2173,8 +2185,7 @@ public virtual InternalEntityTypeBuilder RemoveUnusedImplicitProperties([NotN foreach (var property in properties) { if (property?.Builder != null - && (property.IsShadowProperty() - || property.DeclaringEntityType.IsPropertyBag)) + && property.IsImplicitlyCreated()) { RemovePropertyIfUnused((Property)(object)property, ConfigurationSource.Convention); } @@ -2827,7 +2838,7 @@ private InternalForeignKeyBuilder HasRelationship( dependentProperties: null, principalKey: null, propertyBaseName: navigationProperty?.GetSimpleMemberName(), - required: required, + required, configurationSource); } else @@ -3572,6 +3583,8 @@ private ForeignKey SetOrAddForeignKey( { oldKey.DeclaringEntityType.Builder.RemoveKeyIfUnused(oldKey); } + + propertyBaseName ??= ForeignKeyPropertyDiscoveryConvention.GetPropertyBaseName(foreignKey); } var baseName = string.IsNullOrEmpty(propertyBaseName) @@ -3968,7 +3981,7 @@ private IReadOnlyList CreateUniqueProperties( var currentProperty = currentProperties?.SingleOrDefault(p => p.Name == propertyName); if (currentProperty != null) { - if (currentProperty.IsShadowProperty() + if (((IConventionProperty)currentProperty).IsImplicitlyCreated() && currentProperty.ClrType != clrType && isRequired) { @@ -4044,7 +4057,7 @@ public virtual IReadOnlyList GetOrCreateProperties( else if (configurationSource.HasValue) { if (ConfigurationSource.Convention.Overrides(property.GetTypeConfigurationSource()) - && property.IsShadowProperty() + && (property.IsShadowProperty() || property.IsIndexerProperty()) && (!property.IsNullable || (required && property.GetIsNullableConfigurationSource() == null)) && property.ClrType.IsNullableType()) { @@ -4376,7 +4389,7 @@ private bool CanAddDiscriminatorProperty( { var conflictingProperty = Metadata.FindPropertiesInHierarchy(name).FirstOrDefault(); if (conflictingProperty != null - && conflictingProperty.IsShadowProperty() + && (conflictingProperty.IsShadowProperty() || conflictingProperty.IsIndexerProperty()) && conflictingProperty.ClrType != propertyType && typeConfigurationSource != null && !typeConfigurationSource.Overrides(conflictingProperty.GetTypeConfigurationSource())) diff --git a/src/EFCore/Metadata/Internal/InternalForeignKeyBuilder.cs b/src/EFCore/Metadata/Internal/InternalForeignKeyBuilder.cs index fc96ee5cf19..f8c24aeeca6 100644 --- a/src/EFCore/Metadata/Internal/InternalForeignKeyBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalForeignKeyBuilder.cs @@ -1624,7 +1624,7 @@ public virtual bool CanInvert( /// 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 InternalForeignKeyBuilder ReuniquifyTemporaryProperties(bool force) + public virtual InternalForeignKeyBuilder ReuniquifyImplicitProperties(bool force) { if (!force && (Metadata.GetPropertiesConfigurationSource() != null @@ -2091,7 +2091,7 @@ private bool CanSetPrincipalKey( if (Metadata.GetPropertiesConfigurationSource().Overrides(ConfigurationSource.DataAnnotation) && Metadata.Properties.All( p => ConfigurationSource.Convention.Overrides(p.GetTypeConfigurationSource()) - && p.IsShadowProperty())) + && (p.IsShadowProperty() || p.IsIndexerProperty()))) { oldNameDependentProperties = Metadata.Properties; } @@ -2843,7 +2843,7 @@ private InternalForeignKeyBuilder GetOrCreateRelationshipBuilder( // Issue #15898 var temporaryProperties = dependentProperties?.Where( p => p.GetConfigurationSource() == ConfigurationSource.Convention - && p.IsShadowProperty()).ToList(); + && ((IConventionProperty)p).IsImplicitlyCreated()).ToList(); var tempIndex = temporaryProperties?.Count > 0 && dependentEntityType.FindIndex(temporaryProperties) == null ? dependentEntityType.Builder.HasIndex(temporaryProperties, ConfigurationSource.Convention).Metadata @@ -2851,7 +2851,7 @@ private InternalForeignKeyBuilder GetOrCreateRelationshipBuilder( var temporaryKeyProperties = principalProperties?.Where( p => p.GetConfigurationSource() == ConfigurationSource.Convention - && p.IsShadowProperty()).ToList(); + && ((IConventionProperty)p).IsImplicitlyCreated()).ToList(); var keyTempIndex = temporaryKeyProperties?.Count > 0 && principalEntityType.FindIndex(temporaryKeyProperties) == null ? principalEntityType.Builder.HasIndex(temporaryKeyProperties, ConfigurationSource.Convention).Metadata @@ -2963,7 +2963,7 @@ private InternalForeignKeyBuilder GetOrCreateRelationshipBuilder( shouldThrow: false) && dependentProperties.All( p => ConfigurationSource.Convention.Overrides(p.GetTypeConfigurationSource()) - && p.IsShadowProperty()))) + && (p.IsShadowProperty() || p.IsIndexerProperty())))) { dependentProperties = oldNameDependentProperties ?? dependentProperties; if (principalKey.Properties.Count == dependentProperties.Count) diff --git a/test/EFCore.Cosmos.FunctionalTests/Internal/CosmosDatabaseCreatorTest.cs b/test/EFCore.Cosmos.FunctionalTests/Internal/CosmosDatabaseCreatorTest.cs index 1f76dc5a436..71df8009df0 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Internal/CosmosDatabaseCreatorTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Internal/CosmosDatabaseCreatorTest.cs @@ -89,7 +89,7 @@ public async Task EnsureDeleted_returns_false_when_database_does_not_exist(bool Assert.False(async ? await creator.EnsureDeletedAsync() : creator.EnsureDeleted()); } - public class BloggingContext : DbContext + private class BloggingContext : DbContext { private readonly string _connectionUri; private readonly string _authToken; @@ -119,7 +119,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) public DbSet Blogs { get; set; } } - public class Blog + private class Blog { public int Id { get; set; } } diff --git a/test/EFCore.Tests/ChangeTracking/SkipCollectionEntryTest.cs b/test/EFCore.Tests/ChangeTracking/SkipCollectionEntryTest.cs index d162130319e..996f5860454 100644 --- a/test/EFCore.Tests/ChangeTracking/SkipCollectionEntryTest.cs +++ b/test/EFCore.Tests/ChangeTracking/SkipCollectionEntryTest.cs @@ -420,7 +420,7 @@ public void IsModified_tracks_mutation_of_join_fks() var relatedToChunky2 = context.Entry(chunky2).Collection(e => e.Cherries); var joinEntity = context.ChangeTracker.Entries>() - .Single(e => e.Property("CherryId").CurrentValue == 1 && e.Property("ChunkyId").CurrentValue == 2) + .Single(e => e.Property("CherryId").CurrentValue == 1 && e.Property("ChunkyId").CurrentValue == 2) .Entity; joinEntity["CherryId"] = 2; @@ -498,7 +498,7 @@ public void Setting_IsModified_false_reverts_changes_to_join_table_FKs() var relatedToChunky2 = context.Entry(chunky2).Collection(e => e.Cherries); var joinEntity = context.ChangeTracker.Entries>() - .Single(e => e.Property("CherryId").CurrentValue == 1 && e.Property("ChunkyId").CurrentValue == 2) + .Single(e => e.Property("CherryId").CurrentValue == 1 && e.Property("ChunkyId").CurrentValue == 2) .Entity; joinEntity["CherryId"] = 2; diff --git a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs index 5f6c05ad8b3..b42b882bd82 100644 --- a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs +++ b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs @@ -206,8 +206,8 @@ public virtual void Passes_on_shadow_key_created_explicitly() SetPrimaryKey(entityType); AddProperties(entityType); - var keyProperty = entityType.AddProperty("Key", typeof(int)); - entityType.AddKey(keyProperty); + var keyProperty = ((IConventionEntityType)entityType).AddProperty("Key", typeof(int)); + ((IConventionEntityType)entityType).AddKey(keyProperty); VerifyWarning( CoreResources.LogShadowPropertyCreated(new TestLogger()).GenerateMessage("Key", "A"), model, diff --git a/test/EFCore.Tests/Metadata/Internal/InternalEntityTypeBuilderTest.cs b/test/EFCore.Tests/Metadata/Internal/InternalEntityTypeBuilderTest.cs index afff5816855..b557b5736b0 100644 --- a/test/EFCore.Tests/Metadata/Internal/InternalEntityTypeBuilderTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/InternalEntityTypeBuilderTest.cs @@ -1624,8 +1624,8 @@ public void Property_removes_existing_index_property_for_higher_source_if_type_m Assert.NotNull(replacedPropertyBuilder); Assert.NotSame(propertyBuilder, replacedPropertyBuilder); - Assert.False(replacedPropertyBuilder.Metadata.IsIndexerProperty()); - Assert.True(replacedPropertyBuilder.Metadata.IsShadowProperty()); + Assert.True(replacedPropertyBuilder.Metadata.IsIndexerProperty()); + Assert.False(replacedPropertyBuilder.Metadata.IsShadowProperty()); } [ConditionalFact] diff --git a/test/EFCore.Tests/ModelBuilding/ManyToManyTestBase.cs b/test/EFCore.Tests/ModelBuilding/ManyToManyTestBase.cs index f4f2405bef7..e314460307c 100644 --- a/test/EFCore.Tests/ModelBuilding/ManyToManyTestBase.cs +++ b/test/EFCore.Tests/ModelBuilding/ManyToManyTestBase.cs @@ -408,15 +408,27 @@ public virtual void Can_use_shared_Type_as_join_entity() var shared1 = model.FindEntityType("Shared1"); Assert.NotNull(shared1); Assert.Equal(2, shared1.GetForeignKeys().Count()); + Assert.Equal(new[] + { + nameof(ManyToManyNavPrincipal.Dependents) + nameof(NavDependent.Id), + nameof(NavDependent.ManyToManyPrincipals) + nameof(ManyToManyNavPrincipal.Id) + }, + shared1.GetProperties().Select(p => p.Name)); Assert.True(shared1.HasSharedClrType); Assert.Equal(typeof(Dictionary), shared1.ClrType); var shared2 = model.FindEntityType("Shared2"); Assert.NotNull(shared2); Assert.Equal(2, shared2.GetForeignKeys().Count()); + Assert.Equal(new[] + { + nameof(ManyToManyPrincipalWithField.Dependents) + nameof(DependentWithField.DependentWithFieldId), + nameof(DependentWithField.ManyToManyPrincipals) + nameof(ManyToManyPrincipalWithField.Id), + "Payload" + }, + shared2.GetProperties().Select(p => p.Name)); Assert.True(shared2.HasSharedClrType); Assert.Equal(typeof(Dictionary), shared2.ClrType); - Assert.NotNull(shared2.FindProperty("Payload")); Assert.Equal( CoreStrings.ClashingSharedType(typeof(Dictionary).DisplayName()),