diff --git a/src/EFCore/ChangeTracking/CollectionEntry.cs b/src/EFCore/ChangeTracking/CollectionEntry.cs
index 1a052f675c1..7bb46345ca2 100644
--- a/src/EFCore/ChangeTracking/CollectionEntry.cs
+++ b/src/EFCore/ChangeTracking/CollectionEntry.cs
@@ -82,6 +82,111 @@ private void LocalDetectChanges()
[param: CanBeNull] set => base.CurrentValue = value;
}
+ ///
+ /// Gets or sets a value indicating whether any of foreign key property values associated
+ /// with this navigation property have been modified and should be updated in the database
+ /// when is called.
+ ///
+ public override bool IsModified
+ {
+ get
+ {
+ var stateManager = InternalEntry.StateManager;
+
+ if (Metadata is ISkipNavigation skipNavigation)
+ {
+ if (InternalEntry.EntityState != EntityState.Unchanged
+ && InternalEntry.EntityState != EntityState.Detached)
+ {
+ return true;
+ }
+
+ var joinEntityType = skipNavigation.JoinEntityType;
+ var foreignKey = skipNavigation.ForeignKey;
+ var inverseForeignKey = skipNavigation.Inverse.ForeignKey;
+ foreach (var joinEntry in stateManager.Entries)
+ {
+ if (joinEntry.EntityType == joinEntityType
+ && stateManager.FindPrincipal(joinEntry, foreignKey) == InternalEntry
+ && (joinEntry.EntityState == EntityState.Added
+ || joinEntry.EntityState == EntityState.Deleted
+ || foreignKey.Properties.Any(joinEntry.IsModified)
+ || inverseForeignKey.Properties.Any(joinEntry.IsModified)
+ || (stateManager.FindPrincipal(joinEntry, inverseForeignKey)?.EntityState == EntityState.Deleted)))
+ {
+ return true;
+ }
+ }
+ }
+ else
+ {
+ var navigationValue = CurrentValue;
+ if (navigationValue != null)
+ {
+ var targetEntityType = Metadata.TargetEntityType;
+ var foreignKey = ((INavigation)Metadata).ForeignKey;
+
+ foreach (var relatedEntity in navigationValue)
+ {
+ var relatedEntry = stateManager.TryGetEntry(relatedEntity, targetEntityType);
+
+ if (relatedEntry != null
+ && (relatedEntry.EntityState == EntityState.Added
+ || relatedEntry.EntityState == EntityState.Deleted
+ || foreignKey.Properties.Any(relatedEntry.IsModified)))
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+ set
+ {
+ var stateManager = InternalEntry.StateManager;
+
+ if (Metadata is ISkipNavigation skipNavigation)
+ {
+ var joinEntityType = skipNavigation.JoinEntityType;
+ var foreignKey = skipNavigation.ForeignKey;
+ foreach (var joinEntry in stateManager
+ .GetEntriesForState(added: !value, modified: !value, deleted: !value, unchanged: value).Where(
+ e => e.EntityType == joinEntityType
+ && stateManager.FindPrincipal(e, foreignKey) == InternalEntry)
+ .ToList())
+ {
+ joinEntry.SetEntityState(value ? EntityState.Modified : EntityState.Unchanged);
+ }
+ }
+ else
+ {
+ var foreignKey = ((INavigation)Metadata).ForeignKey;
+ var navigationValue = CurrentValue;
+ if (navigationValue != null)
+ {
+ foreach (var relatedEntity in navigationValue)
+ {
+ var relatedEntry = InternalEntry.StateManager.TryGetEntry(relatedEntity, Metadata.TargetEntityType);
+ if (relatedEntry != null)
+ {
+ var anyNonPk = foreignKey.Properties.Any(p => !p.IsPrimaryKey());
+ foreach (var property in foreignKey.Properties)
+ {
+ if (anyNonPk
+ && !property.IsPrimaryKey())
+ {
+ relatedEntry.SetPropertyModified(property, isModified: value, acceptChanges: false);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
///
///
/// Loads the entities referenced by this navigation property, unless
diff --git a/src/EFCore/ChangeTracking/EntityEntry.cs b/src/EFCore/ChangeTracking/EntityEntry.cs
index e5657dbd160..8a7d3d6a801 100644
--- a/src/EFCore/ChangeTracking/EntityEntry.cs
+++ b/src/EFCore/ChangeTracking/EntityEntry.cs
@@ -141,7 +141,8 @@ public virtual MemberEntry Member([NotNull] string propertyName)
return new PropertyEntry(InternalEntry, propertyName);
}
- var navigation = InternalEntry.EntityType.FindNavigation(propertyName);
+ var navigation = (INavigationBase)InternalEntry.EntityType.FindNavigation(propertyName)
+ ?? InternalEntry.EntityType.FindSkipNavigation(propertyName);
if (navigation != null)
{
return navigation.IsCollection
@@ -170,7 +171,9 @@ public virtual NavigationEntry Navigation([NotNull] string propertyName)
{
Check.NotEmpty(propertyName, nameof(propertyName));
- var navigation = InternalEntry.EntityType.FindNavigation(propertyName);
+ var navigation = (INavigationBase)InternalEntry.EntityType.FindNavigation(propertyName)
+ ?? InternalEntry.EntityType.FindSkipNavigation(propertyName);
+
if (navigation != null)
{
return navigation.IsCollection
diff --git a/src/EFCore/ChangeTracking/Internal/NavigationFixer.cs b/src/EFCore/ChangeTracking/Internal/NavigationFixer.cs
index 5d078f7aaee..48f62f1c39a 100644
--- a/src/EFCore/ChangeTracking/Internal/NavigationFixer.cs
+++ b/src/EFCore/ChangeTracking/Internal/NavigationFixer.cs
@@ -847,7 +847,7 @@ private void InitialFixup(
try
{
_inFixup = false;
- joinEntry.SetEntityState(EntityState.Added);
+ joinEntry.SetEntityState(setModified ? EntityState.Added : EntityState.Unchanged);
}
finally
{
@@ -892,7 +892,7 @@ private void DelayedFixup(
try
{
_inFixup = false;
- joinEntry.SetEntityState(EntityState.Added);
+ joinEntry.SetEntityState(setModified ? EntityState.Added : EntityState.Unchanged);
}
finally
{
diff --git a/src/EFCore/ChangeTracking/NavigationEntry.cs b/src/EFCore/ChangeTracking/NavigationEntry.cs
index 9a0dbe87c6f..6aa906e3403 100644
--- a/src/EFCore/ChangeTracking/NavigationEntry.cs
+++ b/src/EFCore/ChangeTracking/NavigationEntry.cs
@@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
-using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
@@ -47,14 +46,16 @@ protected NavigationEntry([NotNull] InternalEntityEntry internalEntry, [NotNull]
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
[EntityFrameworkInternal]
- protected NavigationEntry([NotNull] InternalEntityEntry internalEntry, [NotNull] INavigation navigation)
+ protected NavigationEntry([NotNull] InternalEntityEntry internalEntry, [NotNull] INavigationBase navigation)
: base(internalEntry, navigation)
{
}
- private static INavigation GetNavigation(InternalEntityEntry internalEntry, string name, bool collection)
+ private static INavigationBase GetNavigation(InternalEntityEntry internalEntry, string name, bool collection)
{
- var navigation = internalEntry.EntityType.FindNavigation(name);
+ var navigation = (INavigationBase)internalEntry.EntityType.FindNavigation(name)
+ ?? internalEntry.EntityType.FindSkipNavigation(name);
+
if (navigation == null)
{
if (internalEntry.EntityType.FindProperty(name) != null)
@@ -177,103 +178,10 @@ public virtual bool IsLoaded
private IEntityFinder TargetFinder
=> InternalEntry.StateManager.CreateEntityFinder(Metadata.TargetEntityType);
- ///
- /// Gets or sets a value indicating whether any of foreign key property values associated
- /// with this navigation property have been modified and should be updated in the database
- /// when is called.
- ///
- public override bool IsModified
- {
- get
- {
- if (Metadata.IsOnDependent)
- {
- return AnyFkPropertiesModified(InternalEntry);
- }
-
- var navigationValue = CurrentValue;
-
- return navigationValue != null
- && (Metadata.IsCollection
- ? ((IEnumerable)navigationValue).OfType