diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerConventionSetBuilder.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerConventionSetBuilder.cs index 0d861724b54..ded1ae336c0 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerConventionSetBuilder.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerConventionSetBuilder.cs @@ -73,6 +73,8 @@ public override ConventionSet CreateConventionSet() ReplaceConvention(conventionSet.ForeignKeyRemovedConventions, valueGenerationConvention); + conventionSet.SkipNavigationForeignKeyChangedConventions.Add(new SqlServerOnDeleteConvention(Dependencies, RelationalDependencies)); + conventionSet.IndexAddedConventions.Add(sqlServerInMemoryTablesConvention); conventionSet.IndexAddedConventions.Add(sqlServerIndexConvention); diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerOnDeleteConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerOnDeleteConvention.cs new file mode 100644 index 00000000000..d1a93db97d0 --- /dev/null +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerOnDeleteConvention.cs @@ -0,0 +1,53 @@ +// 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.Linq; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure; + +// ReSharper disable once CheckNamespace +namespace Microsoft.EntityFrameworkCore.Metadata.Conventions +{ + /// + /// A convention that configures the OnDelete behavior for foreign keys on the join entity type for + /// self-referencing skip navigations + /// + public class SqlServerOnDeleteConvention : ISkipNavigationForeignKeyChangedConvention + { + /// + /// Creates a new instance of . + /// + /// Parameter object containing dependencies for this convention. + /// Parameter object containing relational dependencies for this convention. + public SqlServerOnDeleteConvention( + [NotNull] ProviderConventionSetBuilderDependencies dependencies, + [NotNull] RelationalConventionSetBuilderDependencies relationalDependencies) + { + } + + /// + public virtual void ProcessSkipNavigationForeignKeyChanged( + IConventionSkipNavigationBuilder skipNavigationBuilder, + IConventionForeignKey foreignKey, + IConventionForeignKey oldForeignKey, + IConventionContext context) + { + var selfReferencingSkipNavigation = skipNavigationBuilder.Metadata; + if (foreignKey == null + || foreignKey.DeleteBehavior != DeleteBehavior.Cascade + || selfReferencingSkipNavigation.Inverse == null + || selfReferencingSkipNavigation.TargetEntityType != selfReferencingSkipNavigation.DeclaringEntityType) + { + return; + } + + if (selfReferencingSkipNavigation == selfReferencingSkipNavigation.DeclaringEntityType.GetDeclaredSkipNavigations() + .First(s => s == selfReferencingSkipNavigation || s == selfReferencingSkipNavigation.Inverse)) + { + foreignKey.Builder.OnDelete(DeleteBehavior.ClientCascade); + selfReferencingSkipNavigation.Inverse.ForeignKey?.Builder.OnDelete(null); + } + } + } +} diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationStrategyConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationStrategyConvention.cs index 2c61a5dea40..abb2817fa48 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationStrategyConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationStrategyConvention.cs @@ -23,14 +23,8 @@ public SqlServerValueGenerationStrategyConvention( [NotNull] ProviderConventionSetBuilderDependencies dependencies, [NotNull] RelationalConventionSetBuilderDependencies relationalDependencies) { - Dependencies = dependencies; } - /// - /// Parameter object containing service dependencies. - /// - protected virtual ProviderConventionSetBuilderDependencies Dependencies { get; } - /// /// Called after a model is initialized. /// diff --git a/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs index 43280026618..cad68e9eda2 100644 --- a/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs +++ b/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs @@ -475,7 +475,8 @@ private static IConventionProperty TryGetProperty(IConventionEntityType entityTy { foreach (var property in entityType.GetProperties()) { - if ((!property.IsShadowProperty() || !ConfigurationSource.Convention.Overrides(property.GetConfigurationSource())) + if ((!(property.IsShadowProperty() || entityType.IsPropertyBag && property.IsIndexerProperty()) + || !ConfigurationSource.Convention.Overrides(property.GetConfigurationSource())) && property.Name.Length == prefix.Length + suffix.Length && property.Name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) && property.Name.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)) @@ -801,14 +802,22 @@ public virtual void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, } private static string GetPropertyBaseName(IConventionForeignKey foreignKey) - => foreignKey.DependentToPrincipal?.Name ?? foreignKey.PrincipalEntityType.ShortName(); + => foreignKey.DependentToPrincipal?.Name + ?? foreignKey.GetReferencingSkipNavigations().FirstOrDefault()?.Inverse?.Name + ?? foreignKey.PrincipalEntityType.ShortName(); private static bool HasUniquifiedProperties(IConventionForeignKey foreignKey) { var fkBaseName = GetPropertyBaseName(foreignKey); for (var i = 0; i < foreignKey.Properties.Count; i++) { - var fkPropertyName = foreignKey.Properties[i].Name; + var property = foreignKey.Properties[i]; + if (!ConfigurationSource.Convention.Overrides(property.GetConfigurationSource())) + { + return false; + } + + var fkPropertyName = property.Name; var pkPropertyName = foreignKey.PrincipalKey.Properties[i].Name; if (fkPropertyName.Length != fkBaseName.Length + pkPropertyName.Length || !fkPropertyName.StartsWith(fkBaseName, StringComparison.Ordinal) diff --git a/src/EFCore/Metadata/Conventions/IForeignKeyAddedConvention.cs b/src/EFCore/Metadata/Conventions/IForeignKeyAddedConvention.cs index 15fdaad5f69..aba80b90b92 100644 --- a/src/EFCore/Metadata/Conventions/IForeignKeyAddedConvention.cs +++ b/src/EFCore/Metadata/Conventions/IForeignKeyAddedConvention.cs @@ -14,10 +14,10 @@ public interface IForeignKeyAddedConvention : IConvention /// /// Called after a foreign key is added to the entity type. /// - /// The builder for the foreign key. + /// The builder for the foreign key. /// Additional information associated with convention execution. void ProcessForeignKeyAdded( - [NotNull] IConventionForeignKeyBuilder relationshipBuilder, + [NotNull] IConventionForeignKeyBuilder foreignKeyBuilder, [NotNull] IConventionContext context); } } diff --git a/src/EFCore/Metadata/Conventions/KeyDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/KeyDiscoveryConvention.cs index c5bfcc5836d..7937efb6985 100644 --- a/src/EFCore/Metadata/Conventions/KeyDiscoveryConvention.cs +++ b/src/EFCore/Metadata/Conventions/KeyDiscoveryConvention.cs @@ -124,7 +124,7 @@ protected virtual void TryConfigurePrimaryKey([NotNull] IConventionEntityTypeBui { var manyToManyForeignKeys = entityType.GetForeignKeys() .Where(fk => fk.GetReferencingSkipNavigations().Any(n => n.IsCollection)).ToList(); - if (manyToManyForeignKeys.Count() == 2) + if (manyToManyForeignKeys.Count == 2) { keyProperties.AddRange(manyToManyForeignKeys.SelectMany(fk => fk.Properties)); } diff --git a/src/EFCore/Metadata/Conventions/ManyToManyJoinEntityTypeConvention.cs b/src/EFCore/Metadata/Conventions/ManyToManyJoinEntityTypeConvention.cs index 7724c964e37..5b109fded6d 100644 --- a/src/EFCore/Metadata/Conventions/ManyToManyJoinEntityTypeConvention.cs +++ b/src/EFCore/Metadata/Conventions/ManyToManyJoinEntityTypeConvention.cs @@ -1,6 +1,7 @@ // 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; using System.Linq; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -109,7 +110,12 @@ private void CreateJoinEntityType(IConventionSkipNavigationBuilder skipNavigatio var inverseEntityType = inverseSkipNavigation.DeclaringEntityType; var model = declaringEntityType.Model; - var joinEntityTypeName = declaringEntityType.ShortName() + inverseEntityType.ShortName(); + var joinEntityTypeName = declaringEntityType.ShortName(); + var inverseName = inverseEntityType.ShortName(); + joinEntityTypeName = StringComparer.Ordinal.Compare(joinEntityTypeName, inverseName) < 0 + ? joinEntityTypeName + inverseName + : inverseName + joinEntityTypeName; + if (model.FindEntityType(joinEntityTypeName) != null) { var otherIdentifiers = model.GetEntityTypes().ToDictionary(et => et.Name, et => 0); @@ -147,7 +153,8 @@ private static ForeignKey CreateSkipNavigationForeignKey( .HasRelationship( skipNavigation.DeclaringEntityType, ConfigurationSource.Convention, - required: true) + required: true, + skipNavigation.Inverse.Name) .IsUnique(false, ConfigurationSource.Convention) .Metadata; } diff --git a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs index 117b3932c92..1d39c2e3b09 100644 --- a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs @@ -2796,7 +2796,7 @@ private InternalForeignKeyBuilder HasRelationship( targetEntityType.Builder, dependentProperties: null, principalKey: null, - navigationToPrincipalName: navigationProperty?.GetSimpleMemberName(), + propertyBaseName: navigationProperty?.GetSimpleMemberName(), required: required, configurationSource: configurationSource); } @@ -2812,7 +2812,7 @@ private InternalForeignKeyBuilder HasRelationship( this, dependentProperties: null, principalKey: null, - navigationToPrincipalName: navigationProperty?.GetSimpleMemberName(), + propertyBaseName: navigationProperty?.GetSimpleMemberName(), required: null, configurationSource: configurationSource); } @@ -2904,8 +2904,9 @@ private InternalForeignKeyBuilder HasRelationship( public virtual InternalForeignKeyBuilder HasRelationship( [NotNull] EntityType principalEntityType, ConfigurationSource configurationSource, - bool? required = null) - => HasRelationshipInternal(principalEntityType, principalKey: null, configurationSource, required); + bool? required = null, + [NotNull] string propertyBaseName = null) + => HasRelationshipInternal(principalEntityType, principalKey: null, configurationSource, required, propertyBaseName); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -2917,14 +2918,16 @@ public virtual InternalForeignKeyBuilder HasRelationship( [NotNull] EntityType principalEntityType, [NotNull] Key principalKey, ConfigurationSource configurationSource, - bool? required = null) - => HasRelationshipInternal(principalEntityType, principalKey, configurationSource, required); + bool? required = null, + [NotNull] string propertyBaseName = null) + => HasRelationshipInternal(principalEntityType, principalKey, configurationSource, required, propertyBaseName); private InternalForeignKeyBuilder HasRelationshipInternal( EntityType targetEntityType, Key principalKey, ConfigurationSource configurationSource, - bool? required = null) + bool? required = null, + string propertyBaseName = null) { InternalForeignKeyBuilder relationship; InternalForeignKeyBuilder newRelationship; @@ -2934,7 +2937,7 @@ private InternalForeignKeyBuilder HasRelationshipInternal( targetEntityType.Builder, null, principalKey, - null, + propertyBaseName, required, configurationSource); @@ -3394,14 +3397,14 @@ public virtual InternalForeignKeyBuilder CreateForeignKey( [NotNull] InternalEntityTypeBuilder principalEntityTypeBuilder, [CanBeNull] IReadOnlyList dependentProperties, [CanBeNull] Key principalKey, - [CanBeNull] string navigationToPrincipalName, + [CanBeNull] string propertyBaseName, bool? required, ConfigurationSource configurationSource) { using var batch = ModelBuilder.Metadata.ConventionDispatcher.DelayConventions(); var foreignKey = SetOrAddForeignKey( - null, principalEntityTypeBuilder, - dependentProperties, principalKey, navigationToPrincipalName, required, configurationSource); + foreignKey: null, principalEntityTypeBuilder, dependentProperties, principalKey, + propertyBaseName, required, configurationSource); if (required.HasValue && foreignKey?.IsRequired == required.Value) @@ -3422,14 +3425,14 @@ public virtual InternalForeignKeyBuilder UpdateForeignKey( [NotNull] ForeignKey foreignKey, [CanBeNull] IReadOnlyList dependentProperties, [CanBeNull] Key principalKey, - [CanBeNull] string navigationToPrincipalName, + [CanBeNull] string propertyBaseName, bool? isRequired, ConfigurationSource? configurationSource) { using var batch = ModelBuilder.Metadata.ConventionDispatcher.DelayConventions(); foreignKey = SetOrAddForeignKey( - foreignKey, foreignKey.PrincipalEntityType.Builder, - dependentProperties, principalKey, navigationToPrincipalName, isRequired, configurationSource); + foreignKey, foreignKey.PrincipalEntityType.Builder, dependentProperties, principalKey, + propertyBaseName, isRequired, configurationSource); return (InternalForeignKeyBuilder)batch.Run(foreignKey)?.Builder; } @@ -3439,7 +3442,7 @@ private ForeignKey SetOrAddForeignKey( InternalEntityTypeBuilder principalEntityTypeBuilder, IReadOnlyList dependentProperties, Key principalKey, - string navigationToPrincipalName, + string propertyBaseName, bool? isRequired, ConfigurationSource? configurationSource) { @@ -3527,9 +3530,9 @@ private ForeignKey SetOrAddForeignKey( } } - var baseName = string.IsNullOrEmpty(navigationToPrincipalName) + var baseName = string.IsNullOrEmpty(propertyBaseName) ? principalType.ShortName() - : navigationToPrincipalName; + : propertyBaseName; dependentProperties = CreateUniqueProperties(null, principalKey.Properties, isRequired ?? false, baseName); } @@ -3803,7 +3806,9 @@ public virtual bool ShouldReuniquifyTemporaryProperties([NotNull] ForeignKey for foreignKey.PrincipalKey.Properties.Select(p => p.Name), foreignKey.IsRequired && foreignKey.GetIsRequiredConfigurationSource().Overrides(ConfigurationSource.Convention), - foreignKey.DependentToPrincipal?.Name ?? foreignKey.PrincipalEntityType.ShortName()) + foreignKey.DependentToPrincipal?.Name + ?? foreignKey.ReferencingSkipNavigations?.FirstOrDefault()?.Inverse?.Name + ?? foreignKey.PrincipalEntityType.ShortName()) .Item1; /// diff --git a/src/EFCore/Metadata/Internal/InternalForeignKeyBuilder.cs b/src/EFCore/Metadata/Internal/InternalForeignKeyBuilder.cs index 20a316dbae0..7bf060a8d52 100644 --- a/src/EFCore/Metadata/Internal/InternalForeignKeyBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalForeignKeyBuilder.cs @@ -1622,7 +1622,7 @@ public virtual InternalForeignKeyBuilder ReuniquifyTemporaryProperties(bool forc using var batch = Metadata.DeclaringEntityType.Model.ConventionDispatcher.DelayConventions(); var temporaryProperties = Metadata.Properties.Where( - p => p.IsShadowProperty() + p => (p.IsShadowProperty() || p.DeclaringEntityType.IsPropertyBag && p.IsIndexerProperty()) && ConfigurationSource.Convention.Overrides(p.GetConfigurationSource())).ToList(); var keysToDetach = temporaryProperties.SelectMany( @@ -2465,7 +2465,8 @@ private InternalForeignKeyBuilder ReplaceForeignKey( foreignKey, dependentProperties?.Count == 0 ? null : dependentProperties, principalKey, - navigationToPrincipal?.Name, + navigationToPrincipal?.Name + ?? referencingSkipNavigations?.FirstOrDefault().Navigation?.Inverse?.Name, isRequired, configurationSource: null); @@ -2833,6 +2834,7 @@ private InternalForeignKeyBuilder GetOrCreateRelationshipBuilder( : null; var removedForeignKeys = new List(); + var referencingSkipNavigationName = Metadata.ReferencingSkipNavigations?.FirstOrDefault()?.Inverse?.Name; if (Metadata.Builder == null) { removedForeignKeys.Add(Metadata); @@ -2975,7 +2977,7 @@ private InternalForeignKeyBuilder GetOrCreateRelationshipBuilder( principalEntityType.Builder, dependentProperties, principalKey, - navigationToPrincipal?.Name, + navigationToPrincipal?.Name ?? referencingSkipNavigationName, isRequired, ConfigurationSource.Convention); } diff --git a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs index 74f1532581b..f71b40c5494 100644 --- a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs +++ b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs @@ -1124,15 +1124,15 @@ public virtual void Many_to_many_join_table_stored_in_snapshot() + @" modelBuilder.Entity(""ManyToManyLeftManyToManyRight"", b => { - b.Property(""ManyToManyLeftId"") + b.Property(""LeftsId"") .HasColumnType(""int""); - b.Property(""ManyToManyRightId"") + b.Property(""RightsId"") .HasColumnType(""int""); - b.HasKey(""ManyToManyLeftId"", ""ManyToManyRightId""); + b.HasKey(""LeftsId"", ""RightsId""); - b.HasIndex(""ManyToManyRightId""); + b.HasIndex(""RightsId""); b.ToTable(""ManyToManyLeftManyToManyRight""); }); @@ -1171,13 +1171,13 @@ public virtual void Many_to_many_join_table_stored_in_snapshot() { b.HasOne(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+ManyToManyLeft"", null) .WithMany() - .HasForeignKey(""ManyToManyLeftId"") + .HasForeignKey(""LeftsId"") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); b.HasOne(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+ManyToManyRight"", null) .WithMany() - .HasForeignKey(""ManyToManyRightId"") + .HasForeignKey(""RightsId"") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); });"), @@ -1188,22 +1188,22 @@ public virtual void Many_to_many_join_table_stored_in_snapshot() Assert.Collection(joinEntity.GetDeclaredProperties(), p => { - Assert.Equal("ManyToManyLeftId", p.Name); + Assert.Equal("LeftsId", p.Name); Assert.True(p.IsShadowProperty()); }, p => { - Assert.Equal("ManyToManyRightId", p.Name); + Assert.Equal("RightsId", p.Name); Assert.True(p.IsShadowProperty()); }); Assert.Collection(joinEntity.FindDeclaredPrimaryKey().Properties, p => { - Assert.Equal("ManyToManyLeftId", p.Name); + Assert.Equal("LeftsId", p.Name); }, p => { - Assert.Equal("ManyToManyRightId", p.Name); + Assert.Equal("RightsId", p.Name); }); Assert.Collection(joinEntity.GetDeclaredForeignKeys(), fk => @@ -1217,7 +1217,7 @@ public virtual void Many_to_many_join_table_stored_in_snapshot() Assert.Collection(fk.Properties, p => { - Assert.Equal("ManyToManyLeftId", p.Name); + Assert.Equal("LeftsId", p.Name); }); }, fk => @@ -1231,7 +1231,7 @@ public virtual void Many_to_many_join_table_stored_in_snapshot() Assert.Collection(fk.Properties, p => { - Assert.Equal("ManyToManyRightId", p.Name); + Assert.Equal("RightsId", p.Name); }); }); }); @@ -1254,15 +1254,15 @@ public virtual void Can_override_table_name_for_many_to_many_join_table_stored_i + @" modelBuilder.Entity(""ManyToManyLeftManyToManyRight"", b => { - b.Property(""ManyToManyLeftId"") + b.Property(""LeftsId"") .HasColumnType(""int""); - b.Property(""ManyToManyRightId"") + b.Property(""RightsId"") .HasColumnType(""int""); - b.HasKey(""ManyToManyLeftId"", ""ManyToManyRightId""); + b.HasKey(""LeftsId"", ""RightsId""); - b.HasIndex(""ManyToManyRightId""); + b.HasIndex(""RightsId""); b.ToTable(""MyJoinTable""); }); @@ -1301,13 +1301,13 @@ public virtual void Can_override_table_name_for_many_to_many_join_table_stored_i { b.HasOne(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+ManyToManyLeft"", null) .WithMany() - .HasForeignKey(""ManyToManyLeftId"") + .HasForeignKey(""LeftsId"") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); b.HasOne(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+ManyToManyRight"", null) .WithMany() - .HasForeignKey(""ManyToManyRightId"") + .HasForeignKey(""RightsId"") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); });"), @@ -1319,22 +1319,22 @@ public virtual void Can_override_table_name_for_many_to_many_join_table_stored_i Assert.Collection(joinEntity.GetDeclaredProperties(), p => { - Assert.Equal("ManyToManyLeftId", p.Name); + Assert.Equal("LeftsId", p.Name); Assert.True(p.IsShadowProperty()); }, p => { - Assert.Equal("ManyToManyRightId", p.Name); + Assert.Equal("RightsId", p.Name); Assert.True(p.IsShadowProperty()); }); Assert.Collection(joinEntity.FindDeclaredPrimaryKey().Properties, p => { - Assert.Equal("ManyToManyLeftId", p.Name); + Assert.Equal("LeftsId", p.Name); }, p => { - Assert.Equal("ManyToManyRightId", p.Name); + Assert.Equal("RightsId", p.Name); }); Assert.Collection(joinEntity.GetDeclaredForeignKeys(), fk => @@ -1348,7 +1348,7 @@ public virtual void Can_override_table_name_for_many_to_many_join_table_stored_i Assert.Collection(fk.Properties, p => { - Assert.Equal("ManyToManyLeftId", p.Name); + Assert.Equal("LeftsId", p.Name); }); }, fk => @@ -1362,7 +1362,7 @@ public virtual void Can_override_table_name_for_many_to_many_join_table_stored_i Assert.Collection(fk.Properties, p => { - Assert.Equal("ManyToManyRightId", p.Name); + Assert.Equal("RightsId", p.Name); }); }); }); diff --git a/test/EFCore.Specification.Tests/Query/ManyToManyQueryFixtureBase.cs b/test/EFCore.Specification.Tests/Query/ManyToManyQueryFixtureBase.cs index 25dbf990eb6..a8f6591beff 100644 --- a/test/EFCore.Specification.Tests/Query/ManyToManyQueryFixtureBase.cs +++ b/test/EFCore.Specification.Tests/Query/ManyToManyQueryFixtureBase.cs @@ -165,6 +165,15 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con .WithOne(e => e.ReferenceInverse) .HasForeignKey(e => e.ReferenceInverseId); + // TODO: Remove UsingEntity + modelBuilder.Entity() + .HasMany(e => e.TwoSkipShared) + .WithMany(e => e.OneSkipShared) + .UsingEntity>( + "EntityOneEntityTwo", + r => r.HasOne().WithMany().HasForeignKey("EntityTwoId"), + l => l.HasOne().WithMany().HasForeignKey("EntityOneId")); + // Nav:2 Payload:No Join:Concrete Extra:None modelBuilder.Entity() .HasMany(e => e.TwoSkip) @@ -197,7 +206,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con .WithMany(e => e.SelfSkipPayloadRight) .UsingEntity( l => l.HasOne(x => x.Left).WithMany(x => x.JoinSelfPayloadLeft), - r => r.HasOne(x => x.Right).WithMany(x => x.JoinSelfPayloadRight).OnDelete(DeleteBehavior.ClientCascade)); + r => r.HasOne(x => x.Right).WithMany(x => x.JoinSelfPayloadRight)); // Nav:2 Payload:No Join:Concrete Extra:Inheritance modelBuilder.Entity() @@ -226,15 +235,17 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con l => l.HasOne(x => x.Two).WithMany(e => e.JoinThreeFull)); // Nav:2 Payload:No Join:Shared Extra:Self-ref + // TODO: Remove UsingEntity modelBuilder.Entity() .HasMany(e => e.SelfSkipSharedLeft) .WithMany(e => e.SelfSkipSharedRight) .UsingEntity>( "JoinTwoSelfShared", l => l.HasOne().WithMany().HasForeignKey("LeftId"), - r => r.HasOne().WithMany().HasForeignKey("RightId").OnDelete(DeleteBehavior.NoAction)); + r => r.HasOne().WithMany().HasForeignKey("RightId")); // Nav:2 Payload:No Join:Shared Extra:CompositeKey + // TODO: Remove UsingEntity modelBuilder.Entity() .HasMany(e => e.CompositeKeySkipShared) .WithMany(e => e.TwoSkipShared) @@ -253,9 +264,15 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con r => r.HasOne(x => x.Three).WithMany(x => x.JoinCompositeKeyFull).IsRequired()); // Nav:2 Payload:No Join:Shared Extra:Inheritance - modelBuilder.Entity().HasMany(e => e.RootSkipShared).WithMany(e => e.ThreeSkipShared); + // TODO: Remove UsingEntity + modelBuilder.Entity().HasMany(e => e.RootSkipShared).WithMany(e => e.ThreeSkipShared) + .UsingEntity>( + "EntityRootEntityThree", + r => r.HasOne().WithMany().HasForeignKey("EntityRootId"), + l => l.HasOne().WithMany().HasForeignKey("EntityThreeId")); // Nav:2 Payload:No Join:Shared Extra:Inheritance,CompositeKey + // TODO: Remove UsingEntity modelBuilder.Entity() .HasMany(e => e.RootSkipShared) .WithMany(e => e.CompositeKeySkipShared) diff --git a/test/EFCore.SqlServer.FunctionalTests/ManyToManyTrackingSqlServerTestBase.cs b/test/EFCore.SqlServer.FunctionalTests/ManyToManyTrackingSqlServerTestBase.cs index a3a1ad59ad2..9c0165cbba0 100644 --- a/test/EFCore.SqlServer.FunctionalTests/ManyToManyTrackingSqlServerTestBase.cs +++ b/test/EFCore.SqlServer.FunctionalTests/ManyToManyTrackingSqlServerTestBase.cs @@ -10,7 +10,7 @@ namespace Microsoft.EntityFrameworkCore { public abstract class ManyToManyTrackingSqlServerTestBase : ManyToManyTrackingTestBase - where TFixture : ManyToManyTrackingTestBase.ManyToManyTrackingFixtureBase + where TFixture : ManyToManyTrackingSqlServerTestBase.ManyToManyTrackingSqlServerFixtureBase { protected ManyToManyTrackingSqlServerTestBase(TFixture fixture) : base(fixture) diff --git a/test/EFCore.Tests/Metadata/Conventions/ManyToManyJoinEntityTypeConventionTest.cs b/test/EFCore.Tests/Metadata/Conventions/ManyToManyJoinEntityTypeConventionTest.cs index e40b13fe9d6..b562c6e0744 100644 --- a/test/EFCore.Tests/Metadata/Conventions/ManyToManyJoinEntityTypeConventionTest.cs +++ b/test/EFCore.Tests/Metadata/Conventions/ManyToManyJoinEntityTypeConventionTest.cs @@ -45,8 +45,9 @@ public void Join_entity_type_is_created_for_self_join() RunConvention(firstSkipNav); - Assert.Single(manyToManySelf.Metadata.Model.GetEntityTypes() - .Where(et => et.IsImplicitlyCreatedJoinEntityType)); + var joinEntityType = manyToManySelf.Metadata.Model.GetEntityTypes() + .Single(et => et.IsImplicitlyCreatedJoinEntityType); + Assert.Equal("ManyToManySelfManyToManySelf", joinEntityType.Name); } [ConditionalFact] @@ -222,7 +223,7 @@ public void Join_entity_type_is_created() ConfigurationSource.Convention); skipNavOnFirst.HasInverse(skipNavOnSecond.Metadata, ConfigurationSource.Convention); - RunConvention(skipNavOnFirst); + RunConvention(skipNavOnSecond); var joinEntityType = manyToManyFirst.Metadata.Model.GetEntityTypes() .Single(et => et.IsImplicitlyCreatedJoinEntityType); diff --git a/test/EFCore.Tests/ModelBuilding/ManyToManyTestBase.cs b/test/EFCore.Tests/ModelBuilding/ManyToManyTestBase.cs index 07cb6273edf..d0ef4d2adc6 100644 --- a/test/EFCore.Tests/ModelBuilding/ManyToManyTestBase.cs +++ b/test/EFCore.Tests/ModelBuilding/ManyToManyTestBase.cs @@ -158,8 +158,8 @@ public virtual void Join_type_is_automatically_configured_by_convention() var key = joinEntityType.FindPrimaryKey(); Assert.Equal( new[] { - nameof(ImplicitManyToManyA) + nameof(ImplicitManyToManyA.Id), - nameof(ImplicitManyToManyB) + nameof(ImplicitManyToManyB.Id) }, + nameof(ImplicitManyToManyB.As) + nameof(ImplicitManyToManyA.Id), + nameof(ImplicitManyToManyA.Bs) + nameof(ImplicitManyToManyB.Id) }, key.Properties.Select(p => p.Name)); Assert.DoesNotContain(joinEntityType.GetProperties(), p => !p.IsIndexerProperty());