diff --git a/src/EFCore/ChangeTracking/Internal/KeyPropagator.cs b/src/EFCore/ChangeTracking/Internal/KeyPropagator.cs
index 532f84c1eb4..542c37e1299 100644
--- a/src/EFCore/ChangeTracking/Internal/KeyPropagator.cs
+++ b/src/EFCore/ChangeTracking/Internal/KeyPropagator.cs
@@ -55,7 +55,8 @@ public virtual InternalEntityEntry PropagateValue(InternalEntityEntry entry, IPr
var principalEntry = TryPropagateValue(entry, property);
if (principalEntry == null
- && property.IsKey())
+ && property.IsKey()
+ && !property.IsForeignKeyToSelf())
{
var valueGenerator = TryGetValueGenerator(property);
@@ -153,19 +154,23 @@ private static InternalEntityEntry TryPropagateValue(InternalEntityEntry entry,
if (principalEntry != null)
{
var principalProperty = foreignKey.PrincipalKey.Properties[propertyIndex];
- var principalValue = principalEntry[principalProperty];
- if (!principalProperty.ClrType.IsDefaultValue(principalValue))
+
+ if (principalProperty != property)
{
- if (principalEntry.HasTemporaryValue(principalProperty))
+ var principalValue = principalEntry[principalProperty];
+ if (!principalProperty.ClrType.IsDefaultValue(principalValue))
{
- entry.SetTemporaryValue(property, principalValue);
+ if (principalEntry.HasTemporaryValue(principalProperty))
+ {
+ entry.SetTemporaryValue(property, principalValue);
+ }
+ else
+ {
+ entry[property] = principalValue;
+ }
+
+ return principalEntry;
}
- else
- {
- entry[property] = principalValue;
- }
-
- return principalEntry;
}
}
diff --git a/src/EFCore/Metadata/Internal/PropertyExtensions.cs b/src/EFCore/Metadata/Internal/PropertyExtensions.cs
index 0fad20513cf..60c40f597f6 100644
--- a/src/EFCore/Metadata/Internal/PropertyExtensions.cs
+++ b/src/EFCore/Metadata/Internal/PropertyExtensions.cs
@@ -5,7 +5,7 @@
using System.Linq;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
-using Microsoft.EntityFrameworkCore.ValueGeneration;
+using Microsoft.EntityFrameworkCore.Utilities;
namespace Microsoft.EntityFrameworkCore.Metadata.Internal
{
@@ -95,15 +95,37 @@ public static IProperty GetGenerationProperty([NotNull] this IProperty property)
}
///
- /// Gets a value indicating whether this property requires a to generate
- /// values when new entities are added to the context.
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// 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 static bool RequiresValueGenerator([NotNull] this IProperty property)
=> (property.ValueGenerated.ForAdd()
- && !property.IsForeignKey()
+ && (!property.IsForeignKey() || property.IsForeignKeyToSelf())
&& property.IsKey())
|| property.GetValueGeneratorFactory() != null;
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// 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 static bool IsForeignKeyToSelf([NotNull] this IProperty property)
+ {
+ foreach (var foreignKey in property.GetContainingForeignKeys())
+ {
+ var propertyIndex = foreignKey.Properties.IndexOf(property);
+ if (propertyIndex == foreignKey.PrincipalKey.Properties.IndexOf(property))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
diff --git a/test/EFCore.SqlServer.FunctionalTests/GraphUpdates/GraphUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/GraphUpdates/GraphUpdatesSqlServerTest.cs
index cfd0f7db27e..3ede68550c4 100644
--- a/test/EFCore.SqlServer.FunctionalTests/GraphUpdates/GraphUpdatesSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/GraphUpdates/GraphUpdatesSqlServerTest.cs
@@ -5,6 +5,7 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.TestUtilities;
+using Xunit;
namespace Microsoft.EntityFrameworkCore
{
@@ -73,6 +74,97 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
}
}
+ public class TptIdentity : GraphUpdatesSqlServerTestBase
+ {
+ public TptIdentity(SqlServerFixture fixture)
+ : base(fixture)
+ {
+ }
+
+ [ConditionalFact(Skip = "Issue #22582")]
+ public override void Can_add_multiple_dependents_when_multiple_possible_principal_sides()
+ {
+ }
+
+ [ConditionalFact(Skip = "Issue #22582")]
+ public override void Can_add_valid_first_dependent_when_multiple_possible_principal_sides()
+ {
+ }
+
+ [ConditionalFact(Skip = "Issue #22582")]
+ public override void Can_add_valid_second_dependent_when_multiple_possible_principal_sides()
+ {
+ }
+
+ protected override void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
+ => facade.UseTransaction(transaction.GetDbTransaction());
+
+ public class SqlServerFixture : GraphUpdatesSqlServerFixtureBase
+ {
+ protected override string StoreName { get; } = "GraphTptIdentityUpdatesTest";
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context)
+ {
+ modelBuilder.UseIdentityColumns();
+
+ base.OnModelCreating(modelBuilder, context);
+
+ modelBuilder.Entity().ToTable(nameof(Root));
+ modelBuilder.Entity().ToTable(nameof(Required1));
+ modelBuilder.Entity().ToTable(nameof(Required1Derived));
+ modelBuilder.Entity().ToTable(nameof(Required1MoreDerived));
+ modelBuilder.Entity().ToTable(nameof(Required2Derived));
+ modelBuilder.Entity().ToTable(nameof(Required2MoreDerived));
+ modelBuilder.Entity().ToTable(nameof(Optional1));
+ modelBuilder.Entity().ToTable(nameof(Optional1Derived));
+ modelBuilder.Entity().ToTable(nameof(Optional1MoreDerived));
+ modelBuilder.Entity().ToTable(nameof(Optional2Derived));
+ modelBuilder.Entity().ToTable(nameof(Optional2MoreDerived));
+ modelBuilder.Entity().ToTable(nameof(RequiredSingle1));
+ modelBuilder.Entity().ToTable(nameof(OptionalSingle1));
+ modelBuilder.Entity().ToTable(nameof(OptionalSingle2));
+ modelBuilder.Entity().ToTable(nameof(RequiredNonPkSingle1));
+ modelBuilder.Entity().ToTable(nameof(RequiredNonPkSingle2Derived));
+ modelBuilder.Entity().ToTable(nameof(RequiredNonPkSingle2MoreDerived));
+ modelBuilder.Entity().ToTable(nameof(RequiredAk1));
+ modelBuilder.Entity().ToTable(nameof(RequiredAk1Derived));
+ modelBuilder.Entity().ToTable(nameof(RequiredAk1MoreDerived));
+ modelBuilder.Entity().ToTable(nameof(OptionalAk1));
+ modelBuilder.Entity().ToTable(nameof(OptionalAk1Derived));
+ modelBuilder.Entity().ToTable(nameof(OptionalAk1MoreDerived));
+ modelBuilder.Entity().ToTable(nameof(RequiredSingleAk1));
+ modelBuilder.Entity().ToTable(nameof(OptionalSingleAk1));
+ modelBuilder.Entity().ToTable(nameof(OptionalSingleAk2Derived));
+ modelBuilder.Entity().ToTable(nameof(OptionalSingleAk2MoreDerived));
+ modelBuilder.Entity().ToTable(nameof(RequiredNonPkSingleAk1));
+ modelBuilder.Entity().ToTable(nameof(RequiredAk2));
+ modelBuilder.Entity().ToTable(nameof(RequiredAk2Derived));
+ modelBuilder.Entity().ToTable(nameof(RequiredAk2MoreDerived));
+ modelBuilder.Entity().ToTable(nameof(OptionalAk2));
+ modelBuilder.Entity().ToTable(nameof(OptionalAk2Derived));
+ modelBuilder.Entity().ToTable(nameof(OptionalAk2MoreDerived));
+ modelBuilder.Entity().ToTable(nameof(RequiredSingleAk2));
+ modelBuilder.Entity().ToTable(nameof(RequiredNonPkSingleAk2));
+ modelBuilder.Entity().ToTable(nameof(RequiredNonPkSingleAk2Derived));
+ modelBuilder.Entity().ToTable(nameof(RequiredNonPkSingleAk2MoreDerived));
+ modelBuilder.Entity().ToTable(nameof(OptionalSingleAk2));
+ modelBuilder.Entity().ToTable(nameof(RequiredComposite1));
+ modelBuilder.Entity().ToTable(nameof(OptionalOverlapping2));
+ modelBuilder.Entity().ToTable(nameof(BadCustomer));
+ modelBuilder.Entity().ToTable(nameof(BadOrder));
+ modelBuilder.Entity().ToTable(nameof(QuestTask));
+ modelBuilder.Entity().ToTable(nameof(QuizTask));
+ modelBuilder.Entity().ToTable(nameof(HiddenAreaTask));
+ modelBuilder.Entity().ToTable(nameof(TaskChoice));
+ modelBuilder.Entity().ToTable(nameof(ParentAsAChild));
+ modelBuilder.Entity().ToTable(nameof(ChildAsAParent));
+ modelBuilder.Entity().ToTable(nameof(Poost));
+ modelBuilder.Entity().ToTable(nameof(Bloog));
+ modelBuilder.Entity().ToTable(nameof(Produce));
+ }
+ }
+ }
+
public class Identity : GraphUpdatesSqlServerTestBase
{
public Identity(SqlServerFixture fixture)