Ejemplo n.º 1
0
        private static void ConditionallyNullForeignKeyProperties(
            InternalEntityEntry dependentEntry,
            InternalEntityEntry principalEntry,
            IForeignKey foreignKey)
        {
            var principalProperties = foreignKey.PrincipalKey.Properties;
            var dependentProperties = foreignKey.Properties;

            if (principalEntry != null &&
                principalEntry.EntityState != EntityState.Detached)
            {
                for (var i = 0; i < foreignKey.Properties.Count; i++)
                {
                    if (!StructuralComparisons.StructuralEqualityComparer.Equals(
                            principalEntry[principalProperties[i]],
                            dependentEntry[dependentProperties[i]]))
                    {
                        return;
                    }
                }
            }

            for (var i = 0; i < foreignKey.Properties.Count; i++)
            {
                dependentEntry[dependentProperties[i]] = null;
                dependentEntry.StateManager.UpdateDependentMap(dependentEntry, foreignKey);
                dependentEntry.SetRelationshipSnapshotValue(dependentProperties[i], null);
            }
        }
Ejemplo n.º 2
0
        private static void SetForeignKeyProperties(
            InternalEntityEntry dependentEntry,
            InternalEntityEntry principalEntry,
            IForeignKey foreignKey,
            bool setModified)
        {
            var principalProperties = foreignKey.PrincipalKey.Properties;
            var dependentProperties = foreignKey.Properties;

            for (var i = 0; i < foreignKey.Properties.Count; i++)
            {
                var principalValue    = principalEntry[principalProperties[i]];
                var dependentProperty = dependentProperties[i];

                if (!StructuralComparisons.StructuralEqualityComparer.Equals(
                        dependentEntry[dependentProperty],
                        principalValue) ||
                    (dependentEntry.IsConceptualNull(dependentProperty) &&
                     principalValue != null))
                {
                    dependentEntry.SetProperty(dependentProperty, principalValue, setModified);
                    dependentEntry.StateManager.UpdateDependentMap(dependentEntry, foreignKey);
                    dependentEntry.SetRelationshipSnapshotValue(dependentProperty, principalValue);
                }
            }
        }
Ejemplo n.º 3
0
        private void Unfixup(INavigation navigation, InternalEntityEntry oldPrincipalEntry, InternalEntityEntry dependentEntry)
        {
            var dependentEntity = dependentEntry.Entity;

            if (navigation.IsDependentToPrincipal())
            {
                SetNavigation(navigation.GetSetter(), dependentEntity, null);

                dependentEntry.SetRelationshipSnapshotValue(navigation, null);
            }
            else
            {
                if (navigation.IsCollection())
                {
                    var collectionAccessor = navigation.GetCollectionAccessor();
                    if (collectionAccessor.Contains(oldPrincipalEntry.Entity, dependentEntity))
                    {
                        collectionAccessor.Remove(oldPrincipalEntry.Entity, dependentEntity);
                        oldPrincipalEntry.RemoveFromCollectionSnapshot(navigation, dependentEntity);
                    }
                }
                else
                {
                    SetNavigation(navigation.GetSetter(), oldPrincipalEntry.Entity, null);
                    oldPrincipalEntry.SetRelationshipSnapshotValue(navigation, null);
                }
            }
        }
Ejemplo n.º 4
0
        private static void SetForeignKeyProperties(
            InternalEntityEntry dependentEntry,
            InternalEntityEntry principalEntry,
            IForeignKey foreignKey,
            bool setModified,
            bool fromQuery)
        {
            var principalProperties = foreignKey.PrincipalKey.Properties;
            var dependentProperties = foreignKey.Properties;

            for (var i = 0; i < foreignKey.Properties.Count; i++)
            {
                var principalProperty = principalProperties[i];
                var dependentProperty = dependentProperties[i];
                var principalValue    = principalEntry[principalProperty];
                var dependentValue    = dependentEntry[dependentProperty];

                if (!PrincipalValueEqualsDependentValue(principalProperty, dependentValue, principalValue) ||
                    (dependentEntry.IsConceptualNull(dependentProperty) &&
                     principalValue != null))
                {
                    if (principalEntry.HasTemporaryValue(principalProperty))
                    {
                        dependentEntry.SetTemporaryValue(dependentProperty, principalValue, setModified);
                    }
                    else
                    {
                        dependentEntry.SetProperty(dependentProperty, principalValue, fromQuery, setModified);
                    }

                    dependentEntry.StateManager.UpdateDependentMap(dependentEntry, foreignKey);
                    dependentEntry.SetRelationshipSnapshotValue(dependentProperty, principalValue);
                }
            }
        }
Ejemplo n.º 5
0
 private static void SetNullForeignKey(InternalEntityEntry dependentEntry, IReadOnlyList <IProperty> dependentProperties)
 {
     foreach (var dependentProperty in dependentProperties)
     {
         dependentEntry[dependentProperty] = null;
         dependentEntry.SetRelationshipSnapshotValue(dependentProperty, null);
     }
 }
Ejemplo n.º 6
0
        private void ConditionallyNullForeignKeyProperties(
            InternalEntityEntry dependentEntry,
            InternalEntityEntry principalEntry,
            IForeignKey foreignKey)
        {
            var principalProperties  = foreignKey.PrincipalKey.Properties;
            var dependentProperties  = foreignKey.Properties;
            var hasOnlyKeyProperties = true;

            if (principalEntry != null &&
                principalEntry.EntityState != EntityState.Detached)
            {
                for (var i = 0; i < foreignKey.Properties.Count; i++)
                {
                    if (!PrincipalValueEqualsDependentValue(
                            principalProperties[i],
                            dependentEntry[dependentProperties[i]],
                            principalEntry[principalProperties[i]]))
                    {
                        return;
                    }

                    if (!dependentProperties[i].IsKey())
                    {
                        hasOnlyKeyProperties = false;
                    }
                }
            }

            for (var i = 0; i < foreignKey.Properties.Count; i++)
            {
                if (!dependentProperties[i].IsKey())
                {
                    dependentEntry[dependentProperties[i]] = null;
                    dependentEntry.StateManager.UpdateDependentMap(dependentEntry, foreignKey);
                    dependentEntry.SetRelationshipSnapshotValue(dependentProperties[i], null);
                }
            }

            if (foreignKey.IsRequired &&
                hasOnlyKeyProperties &&
                dependentEntry.EntityState != EntityState.Detached)
            {
                switch (dependentEntry.EntityState)
                {
                case EntityState.Added:
                    dependentEntry.SetEntityState(EntityState.Detached);
                    DeleteFixup(dependentEntry);
                    break;

                case EntityState.Unchanged:
                case EntityState.Modified:
                    dependentEntry.SetEntityState(EntityState.Deleted);
                    DeleteFixup(dependentEntry);
                    break;
                }
            }
        }
Ejemplo n.º 7
0
 private static void SetForeignKeyValue(IForeignKey foreignKey, InternalEntityEntry dependentEntry, IReadOnlyList <object> principalValues)
 {
     for (var i = 0; i < foreignKey.Properties.Count; i++)
     {
         var principalValue = principalValues[i];
         if ((foreignKey.Properties[i].GetGenerationProperty() == null) ||
             !foreignKey.PrincipalKey.Properties[i].ClrType.IsDefaultValue(principalValue))
         {
             var dependentProperty = foreignKey.Properties[i];
             dependentEntry[dependentProperty] = principalValue;
             dependentEntry.SetRelationshipSnapshotValue(dependentProperty, principalValue);
         }
     }
 }
Ejemplo n.º 8
0
        private void StealReference(IForeignKey foreignKey, InternalEntityEntry dependentEntry)
        {
            var navigation = foreignKey.DependentToPrincipal;

            if (navigation != null)
            {
                SetNavigation(navigation.GetSetter(), dependentEntry.Entity, null);
                dependentEntry.SetRelationshipSnapshotValue(navigation, null);
            }

            foreach (var property in foreignKey.Properties.Where(p => p.IsNullable).ToList())
            {
                dependentEntry[property] = null;
            }
        }
Ejemplo n.º 9
0
 private void SetNavigation(InternalEntityEntry entry, INavigation navigation, object value)
 {
     if (navigation != null)
     {
         _changeDetector.Suspend();
         try
         {
             entry[navigation] = value;
         }
         finally
         {
             _changeDetector.Resume();
         }
         entry.SetRelationshipSnapshotValue(navigation, value);
     }
 }
Ejemplo n.º 10
0
        private static void SetForeignKeyProperties(
            InternalEntityEntry dependentEntry,
            InternalEntityEntry principalEntry,
            IForeignKey foreignKey,
            bool setModified)
        {
            var principalProperties = foreignKey.PrincipalKey.Properties;
            var dependentProperties = foreignKey.Properties;

            for (var i = 0; i < foreignKey.Properties.Count; i++)
            {
                var principalValue = principalEntry[principalProperties[i]];
                dependentEntry.SetProperty(dependentProperties[i], principalValue, setModified);
                dependentEntry.StateManager.UpdateDependentMap(dependentEntry, foreignKey);
                dependentEntry.SetRelationshipSnapshotValue(dependentProperties[i], principalValue);
            }
        }
Ejemplo n.º 11
0
 private void SetNavigation(InternalEntityEntry entry, INavigation navigation, InternalEntityEntry value)
 {
     if (navigation != null)
     {
         _changeDetector.Suspend();
         var entity = value?.Entity;
         try
         {
             entry[navigation] = entity;
         }
         finally
         {
             _changeDetector.Resume();
         }
         entry.SetRelationshipSnapshotValue(navigation, entity);
     }
 }
Ejemplo n.º 12
0
        private void DoFixup(IEnumerable <INavigation> navigations, InternalEntityEntry principalEntry, InternalEntityEntry[] dependentEntries)
        {
            foreach (var navigation in navigations)
            {
                if (navigation.IsDependentToPrincipal())
                {
                    var setter = navigation.GetSetter();

                    foreach (var dependent in dependentEntries)
                    {
                        SetNavigation(setter, dependent.Entity, principalEntry.Entity);
                        dependent.SetRelationshipSnapshotValue(navigation, principalEntry.Entity);
                    }
                }
                else
                {
                    if (navigation.IsCollection())
                    {
                        var collectionAccessor = navigation.GetCollectionAccessor();

                        foreach (var dependent in dependentEntries)
                        {
                            var dependentEntity = dependent.Entity;
                            if (!collectionAccessor.Contains(principalEntry.Entity, dependentEntity))
                            {
                                collectionAccessor.Add(principalEntry.Entity, dependentEntity);
                                principalEntry.AddToCollectionSnapshot(navigation, dependentEntity);
                            }
                        }
                    }
                    else
                    {
                        // TODO: Decide how to handle case where multiple values match non-collection nav prop
                        // Issue #739
                        var value = dependentEntries.Single().Entity;
                        SetNavigation(navigation.GetSetter(), principalEntry.Entity, value);
                        principalEntry.SetRelationshipSnapshotValue(navigation, value);
                    }
                }
            }
        }
Ejemplo n.º 13
0
        private static void DetectKeyChange(InternalEntityEntry entry, IProperty property)
        {
            var keys        = property.FindContainingKeys().ToList();
            var foreignKeys = property.FindContainingForeignKeys().ToList();

            if ((keys.Count > 0) ||
                (foreignKeys.Count > 0))
            {
                var snapshotValue = entry.GetRelationshipSnapshotValue(property);
                var currentValue  = entry[property];

                // Note that mutation of a byte[] key is not supported or detected, but two different instances
                // of byte[] with the same content must be detected as equal.
                if (!StructuralComparisons.StructuralEqualityComparer.Equals(currentValue, snapshotValue))
                {
                    var stateManager = entry.StateManager;

                    if (foreignKeys.Count > 0)
                    {
                        stateManager.Notify.ForeignKeyPropertyChanged(entry, property, snapshotValue, currentValue);

                        foreach (var foreignKey in foreignKeys)
                        {
                            stateManager.UpdateDependentMap(entry, foreignKey);
                        }
                    }

                    if (keys.Count > 0)
                    {
                        foreach (var key in keys)
                        {
                            stateManager.UpdateIdentityMap(entry, key);
                        }

                        stateManager.Notify.PrincipalKeyPropertyChanged(entry, property, snapshotValue, currentValue);
                    }

                    entry.SetRelationshipSnapshotValue(property, currentValue);
                }
            }
        }
Ejemplo n.º 14
0
        /// <summary>
        ///     This API supports the Entity Framework Core infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public virtual void NavigationReferenceChanged(InternalEntityEntry entry, INavigation navigation, object oldValue, object newValue)
        {
            if (_inFixup)
            {
                return;
            }

            var foreignKey   = navigation.ForeignKey;
            var stateManager = entry.StateManager;
            var inverse      = navigation.FindInverse();

            var oldTargetEntry = oldValue == null ? null : stateManager.TryGetEntry(oldValue);

            if (oldTargetEntry?.EntityState == EntityState.Detached)
            {
                oldTargetEntry = null;
            }

            var newTargetEntry = newValue == null ? null : stateManager.TryGetEntry(newValue);

            if (newTargetEntry?.EntityState == EntityState.Detached)
            {
                newTargetEntry = null;
            }

            try
            {
                _inFixup = true;

                if (navigation.IsDependentToPrincipal())
                {
                    if (newValue != null)
                    {
                        if (newTargetEntry != null)
                        {
                            if (foreignKey.IsUnique)
                            {
                                // Navigation points to principal. Find the dependent that previously pointed to that principal and
                                // null out its FKs and navigation property. A.k.a. reference stealing.
                                // However, if the FK has already been changed or the reference is already set to point
                                // to something else, then don't change it.
                                var victimDependentEntry = stateManager.GetDependents(newTargetEntry, foreignKey).FirstOrDefault();
                                if (victimDependentEntry != null &&
                                    victimDependentEntry != entry)
                                {
                                    ConditionallyNullForeignKeyProperties(victimDependentEntry, newTargetEntry, foreignKey);

                                    if (ReferenceEquals(victimDependentEntry[navigation], newTargetEntry.Entity))
                                    {
                                        SetNavigation(victimDependentEntry, navigation, null);
                                    }
                                }
                            }

                            // Set the FK properties to reflect the change to the navigation.
                            SetForeignKeyProperties(entry, newTargetEntry, foreignKey, setModified: true);
                        }
                    }
                    else
                    {
                        // Null the FK properties to reflect that the navigation has been nulled out.
                        ConditionallyNullForeignKeyProperties(entry, oldTargetEntry, foreignKey);
                    }

                    if (inverse != null)
                    {
                        var collectionAccessor = inverse.IsCollection() ? inverse.GetCollectionAccessor() : null;

                        // Set the inverse reference or add the entity to the inverse collection
                        if (newTargetEntry != null)
                        {
                            SetReferenceOrAddToCollection(newTargetEntry, inverse, collectionAccessor, entry.Entity);
                        }

                        // Remove the entity from the old collection, or null the old inverse unless it was already
                        // changed to point to something else
                        if (oldTargetEntry != null)
                        {
                            if (collectionAccessor != null)
                            {
                                RemoveFromCollection(oldTargetEntry, inverse, collectionAccessor, entry.Entity);
                            }
                            else if (ReferenceEquals(oldTargetEntry[inverse], entry.Entity))
                            {
                                SetNavigation(oldTargetEntry, inverse, null);
                            }
                        }
                    }
                }
                else
                {
                    Debug.Assert(foreignKey.IsUnique);

                    if (newTargetEntry != null)
                    {
                        // Navigation points to dependent and is 1:1. Find the principal that previously pointed to that
                        // dependent and null out its navigation property. A.k.a. reference stealing.
                        // However, if the reference is already set to point to something else, then don't change it.
                        var victimPrincipalEntry = stateManager.GetPrincipal(newTargetEntry, foreignKey);
                        if (victimPrincipalEntry != null &&
                            victimPrincipalEntry != entry &&
                            ReferenceEquals(victimPrincipalEntry[navigation], newTargetEntry.Entity))
                        {
                            SetNavigation(victimPrincipalEntry, navigation, null);
                        }

                        SetForeignKeyProperties(newTargetEntry, entry, foreignKey, setModified: true);

                        SetNavigation(newTargetEntry, inverse, entry.Entity);
                    }

                    if (oldTargetEntry != null)
                    {
                        // Null the FK properties on the old dependent, unless they have already been changed
                        ConditionallyNullForeignKeyProperties(oldTargetEntry, entry, foreignKey);

                        // Clear the inverse reference, unless it has already been changed
                        if (inverse != null &&
                            ReferenceEquals(oldTargetEntry[inverse], entry.Entity))
                        {
                            SetNavigation(oldTargetEntry, inverse, null);
                        }
                    }
                }
            }
            finally
            {
                _inFixup = false;
            }

            if (newValue != null &&
                newTargetEntry == null)
            {
                stateManager.RecordReferencedUntrackedEntity(newValue, navigation, entry);
                entry.SetRelationshipSnapshotValue(navigation, newValue);
                _attacher.AttachGraph(stateManager.GetOrCreateEntry(newValue), EntityState.Added);
            }
        }
Ejemplo n.º 15
0
        /// <summary>
        ///     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.
        /// </summary>
        public virtual void NavigationReferenceChanged(InternalEntityEntry entry, INavigation navigation, object oldValue, object newValue)
        {
            if (_inFixup)
            {
                return;
            }

            var foreignKey       = navigation.ForeignKey;
            var stateManager     = entry.StateManager;
            var inverse          = navigation.Inverse;
            var targetEntityType = navigation.TargetEntityType;

            var oldTargetEntry = oldValue == null ? null : stateManager.TryGetEntry(oldValue, targetEntityType);

            if (oldTargetEntry?.EntityState == EntityState.Detached)
            {
                oldTargetEntry = null;
            }

            var newTargetEntry = newValue == null ? null : stateManager.TryGetEntry(newValue, targetEntityType);

            if (newTargetEntry?.EntityState == EntityState.Detached)
            {
                newTargetEntry = null;
            }

            try
            {
                _inFixup = true;

                if (navigation.IsOnDependent)
                {
                    if (newValue != null)
                    {
                        if (newTargetEntry != null)
                        {
                            if (foreignKey.IsUnique)
                            {
                                // Navigation points to principal. Find the dependent that previously pointed to that principal and
                                // null out its FKs and navigation property. A.k.a. reference stealing.
                                // However, if the FK has already been changed or the reference is already set to point
                                // to something else, then don't change it.
                                var victimDependentEntry = stateManager.GetDependents(newTargetEntry, foreignKey).FirstOrDefault();
                                if (victimDependentEntry != null &&
                                    victimDependentEntry != entry)
                                {
                                    ConditionallyNullForeignKeyProperties(victimDependentEntry, newTargetEntry, foreignKey);

                                    if (ReferenceEquals(victimDependentEntry[navigation], newTargetEntry.Entity) &&
                                        victimDependentEntry.StateManager
                                        .TryGetEntry(victimDependentEntry.Entity, navigation.DeclaringEntityType)
                                        != null)
                                    {
                                        SetNavigation(victimDependentEntry, navigation, null, fromQuery: false);
                                    }
                                }
                            }

                            // Set the FK properties to reflect the change to the navigation.
                            SetForeignKeyProperties(entry, newTargetEntry, foreignKey, setModified: true, fromQuery: false);
                        }
                    }
                    else
                    {
                        // Null the FK properties to reflect that the navigation has been nulled out.
                        ConditionallyNullForeignKeyProperties(entry, oldTargetEntry, foreignKey);
                        entry.SetRelationshipSnapshotValue(navigation, null);
                    }

                    if (inverse != null)
                    {
                        // Set the inverse reference or add the entity to the inverse collection
                        if (newTargetEntry != null)
                        {
                            SetReferenceOrAddToCollection(newTargetEntry, inverse, entry, fromQuery: false);
                        }

                        // Remove the entity from the old collection, or null the old inverse unless it was already
                        // changed to point to something else
                        if (oldTargetEntry != null &&
                            oldTargetEntry.EntityState != EntityState.Deleted)
                        {
                            ResetReferenceOrRemoveCollection(oldTargetEntry, inverse, entry, fromQuery: false);
                        }
                    }
                }
                else
                {
                    Check.DebugAssert(foreignKey.IsUnique, $"foreignKey {foreignKey} is not unique");

                    if (oldTargetEntry != null)
                    {
                        // Null the FK properties on the old dependent, unless they have already been changed
                        ConditionallyNullForeignKeyProperties(oldTargetEntry, entry, foreignKey);

                        // Clear the inverse reference, unless it has already been changed
                        if (inverse != null &&
                            ReferenceEquals(oldTargetEntry[inverse], entry.Entity) &&
                            (!oldTargetEntry.EntityType.HasDefiningNavigation() ||
                             entry.EntityType.GetNavigations().All(
                                 n => n == navigation ||
                                 !ReferenceEquals(oldTargetEntry.Entity, entry[n]))))
                        {
                            SetNavigation(oldTargetEntry, inverse, null, fromQuery: false);
                        }
                    }

                    if (newTargetEntry != null)
                    {
                        // Navigation points to dependent and is 1:1. Find the principal that previously pointed to that
                        // dependent and null out its navigation property. A.k.a. reference stealing.
                        // However, if the reference is already set to point to something else, then don't change it.
                        var victimPrincipalEntry = stateManager.FindPrincipal(newTargetEntry, foreignKey);
                        if (victimPrincipalEntry != null &&
                            victimPrincipalEntry != entry &&
                            ReferenceEquals(victimPrincipalEntry[navigation], newTargetEntry.Entity))
                        {
                            SetNavigation(victimPrincipalEntry, navigation, null, fromQuery: false);
                        }

                        SetForeignKeyProperties(newTargetEntry, entry, foreignKey, setModified: true, fromQuery: false);

                        SetNavigation(newTargetEntry, inverse, entry, fromQuery: false);
                    }
                }

                if (newValue == null)
                {
                    entry.SetIsLoaded(navigation, loaded: false);
                }
            }
            finally
            {
                _inFixup = false;
            }

            if (newValue != null &&
                newTargetEntry == null)
            {
                stateManager.RecordReferencedUntrackedEntity(newValue, navigation, entry);
                entry.SetRelationshipSnapshotValue(navigation, newValue);

                newTargetEntry = stateManager.GetOrCreateEntry(newValue, targetEntityType);

                _attacher.AttachGraph(
                    newTargetEntry,
                    EntityState.Added,
                    EntityState.Modified,
                    forceStateWhenUnknownKey: false);
            }
        }
Ejemplo n.º 16
0
        /// <summary>
        ///     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.
        /// </summary>
        public virtual void KeyPropertyChanged(
            InternalEntityEntry entry,
            IProperty property,
            IReadOnlyList <IKey> containingPrincipalKeys,
            IReadOnlyList <IForeignKey> containingForeignKeys,
            object oldValue,
            object newValue)
        {
            if (entry.EntityState == EntityState.Detached)
            {
                return;
            }

            try
            {
                _inFixup = true;

                var stateManager = entry.StateManager;

                foreach (var foreignKey in containingForeignKeys)
                {
                    var newPrincipalEntry = stateManager.FindPrincipal(entry, foreignKey)
                                            ?? stateManager.FindPrincipalUsingPreStoreGeneratedValues(entry, foreignKey);
                    var oldPrincipalEntry = stateManager.FindPrincipalUsingRelationshipSnapshot(entry, foreignKey);

                    var principalToDependent = foreignKey.PrincipalToDependent;
                    if (principalToDependent != null)
                    {
                        if (oldPrincipalEntry != null &&
                            oldPrincipalEntry.EntityState != EntityState.Deleted)
                        {
                            // Remove this entity from the principal collection that it was previously part of,
                            // or null the navigation for a 1:1 unless that reference was already changed.
                            ResetReferenceOrRemoveCollection(oldPrincipalEntry, principalToDependent, entry, fromQuery: false);
                        }

                        if (newPrincipalEntry != null &&
                            !entry.IsConceptualNull(property))
                        {
                            // Add this entity to the collection of the new principal, or set the navigation for a 1:1
                            SetReferenceOrAddToCollection(newPrincipalEntry, principalToDependent, entry, fromQuery: false);
                        }
                    }

                    var dependentToPrincipal = foreignKey.DependentToPrincipal;
                    if (dependentToPrincipal != null)
                    {
                        if (newPrincipalEntry != null)
                        {
                            if (foreignKey.IsUnique)
                            {
                                // Dependent has been changed to point to a new principal.
                                // Find the dependent that previously pointed to the new principal and null out its FKs
                                // and navigation property. A.k.a. reference stealing.
                                // However, if the FK has already been changed or the reference is already set to point
                                // to something else, then don't change it.
                                var targetDependentEntry
                                    = stateManager.GetDependentsUsingRelationshipSnapshot(newPrincipalEntry, foreignKey).FirstOrDefault();

                                if (targetDependentEntry != null &&
                                    targetDependentEntry != entry)
                                {
                                    ConditionallyNullForeignKeyProperties(targetDependentEntry, newPrincipalEntry, foreignKey);

                                    if (ReferenceEquals(targetDependentEntry[dependentToPrincipal], newPrincipalEntry.Entity) &&
                                        targetDependentEntry.StateManager.TryGetEntry(
                                            targetDependentEntry.Entity, foreignKey.DeclaringEntityType)
                                        != null)
                                    {
                                        SetNavigation(targetDependentEntry, dependentToPrincipal, null, fromQuery: false);
                                    }
                                }
                            }

                            if (!entry.IsConceptualNull(property))
                            {
                                SetNavigation(entry, dependentToPrincipal, newPrincipalEntry, fromQuery: false);
                            }
                        }
                        else if (oldPrincipalEntry != null)
                        {
                            if (ReferenceEquals(entry[dependentToPrincipal], oldPrincipalEntry.Entity) &&
                                entry.StateManager.TryGetEntry(entry.Entity, foreignKey.DeclaringEntityType) != null)
                            {
                                SetNavigation(entry, dependentToPrincipal, null, fromQuery: false);
                            }
                        }
                        else
                        {
                            if (entry[dependentToPrincipal] == null &&
                                entry.StateManager.TryGetEntry(entry.Entity, foreignKey.DeclaringEntityType) != null)
                            {
                                // FK has changed but navigation is still null
                                entry.SetIsLoaded(dependentToPrincipal, false);
                            }
                        }
                    }

                    stateManager.UpdateDependentMap(entry, foreignKey);
                }

                foreach (var key in containingPrincipalKeys)
                {
                    stateManager.UpdateIdentityMap(entry, key);

                    // Propagate principal key values into FKs
                    foreach (var foreignKey in key.GetReferencingForeignKeys())
                    {
                        foreach (var dependentEntry in stateManager
                                 .GetDependentsUsingRelationshipSnapshot(entry, foreignKey).ToList())
                        {
                            SetForeignKeyProperties(dependentEntry, entry, foreignKey, setModified: true, fromQuery: false);
                        }

                        if (foreignKey.IsOwnership)
                        {
                            continue;
                        }

                        // Fix up dependents that have been added by propagating through different foreign key
                        foreach (var dependentEntry in stateManager.GetDependents(entry, foreignKey).ToList())
                        {
                            var principalToDependent = foreignKey.PrincipalToDependent;
                            if (principalToDependent != null)
                            {
                                if (!entry.IsConceptualNull(property))
                                {
                                    // Add this entity to the collection of the new principal, or set the navigation for a 1:1
                                    SetReferenceOrAddToCollection(entry, principalToDependent, dependentEntry, fromQuery: false);
                                }
                            }

                            var dependentToPrincipal = foreignKey.DependentToPrincipal;
                            if (dependentToPrincipal != null)
                            {
                                if (!entry.IsConceptualNull(property))
                                {
                                    SetNavigation(dependentEntry, dependentToPrincipal, entry, fromQuery: false);
                                }
                            }
                        }
                    }
                }

                entry.SetRelationshipSnapshotValue(property, newValue);
            }
            finally
            {
                _inFixup = false;
            }
        }
Ejemplo n.º 17
0
        private void InitialFixup(InternalEntityEntry entry, bool fromQuery)
        {
            var entityType   = entry.EntityType;
            var stateManager = entry.StateManager;

            foreach (var foreignKey in entityType.GetForeignKeys())
            {
                var principalEntry = stateManager.GetPrincipal(entry, foreignKey);
                if (principalEntry != null)
                {
                    // Set navigation to principal based on FK properties
                    SetNavigation(entry, foreignKey.DependentToPrincipal, principalEntry.Entity);

                    // Add this entity to principal's collection, or set inverse for 1:1
                    var principalToDependent = foreignKey.PrincipalToDependent;
                    if (principalToDependent != null)
                    {
                        SetReferenceOrAddToCollection(
                            principalEntry,
                            principalToDependent,
                            principalToDependent.IsCollection() ? principalToDependent.GetCollectionAccessor() : null,
                            entry.Entity);
                    }
                }
            }

            foreach (var foreignKey in entityType.GetReferencingForeignKeys())
            {
                var dependents = stateManager.GetDependents(entry, foreignKey).ToList();
                if (dependents.Any())
                {
                    var dependentToPrincipal = foreignKey.DependentToPrincipal;
                    var principalToDependent = foreignKey.PrincipalToDependent;

                    if (foreignKey.IsUnique)
                    {
                        var dependentEntry = dependents.First();

                        // Set navigations to and from principal entity that is indicated by FK
                        SetNavigation(entry, principalToDependent, dependentEntry.Entity);
                        SetNavigation(dependentEntry, dependentToPrincipal, entry.Entity);
                    }
                    else
                    {
                        var collectionAccessor = principalToDependent?.GetCollectionAccessor();

                        foreach (var dependentEntry in dependents)
                        {
                            var dependentEntity = dependentEntry.Entity;

                            // Add to collection on principal indicated by FK and set inverse navigation
                            AddToCollection(entry, principalToDependent, collectionAccessor, dependentEntity);

                            SetNavigation(dependentEntry, dependentToPrincipal, entry.Entity);
                        }
                    }
                }
            }

            // If the new state is from a query then we are going to assume that the FK value is the source of
            // truth and not attempt to ascertain relationships from navigation properties
            if (!fromQuery)
            {
                foreach (var foreignKey in entityType.GetReferencingForeignKeys())
                {
                    var principalToDependent = foreignKey.PrincipalToDependent;
                    if (principalToDependent != null)
                    {
                        var navigationValue = entry[principalToDependent];
                        if (navigationValue != null)
                        {
                            var setModified = entry.EntityState != EntityState.Unchanged;

                            if (principalToDependent.IsCollection())
                            {
                                var dependents = ((IEnumerable)navigationValue).Cast <object>();
                                foreach (var dependentEntity in dependents)
                                {
                                    var dependentEntry = stateManager.TryGetEntry(dependentEntity);
                                    if (dependentEntry == null ||
                                        dependentEntry.EntityState == EntityState.Detached)
                                    {
                                        // If dependents in collection are not yet tracked, then save them away so that
                                        // when we start tracking them we can come back and fixup this principal to them
                                        stateManager.RecordReferencedUntrackedEntity(dependentEntity, principalToDependent, entry);
                                    }
                                    else
                                    {
                                        FixupToDependent(entry, dependentEntry, foreignKey, setModified);
                                    }
                                }
                            }
                            else
                            {
                                var dependentEntry = stateManager.TryGetEntry(navigationValue);
                                if (dependentEntry == null ||
                                    dependentEntry.EntityState == EntityState.Detached)
                                {
                                    // If dependent is not yet tracked, then save it away so that
                                    // when we start tracking it we can come back and fixup this principal to it
                                    stateManager.RecordReferencedUntrackedEntity(navigationValue, principalToDependent, entry);
                                }
                                else
                                {
                                    FixupToDependent(entry, dependentEntry, foreignKey, setModified);
                                }
                            }
                        }
                    }
                }

                foreach (var foreignKey in entityType.GetForeignKeys())
                {
                    var dependentToPrincipal = foreignKey.DependentToPrincipal;
                    if (dependentToPrincipal != null)
                    {
                        var navigationValue = entry[dependentToPrincipal];
                        if (navigationValue != null)
                        {
                            var principalEntry = stateManager.TryGetEntry(navigationValue);
                            if (principalEntry == null ||
                                principalEntry.EntityState == EntityState.Detached)
                            {
                                // If principal is not yet tracked, then save it away so that
                                // when we start tracking it we can come back and fixup this dependent to it
                                stateManager.RecordReferencedUntrackedEntity(navigationValue, dependentToPrincipal, entry);
                            }
                            else
                            {
                                FixupToPrincipal(entry, principalEntry, foreignKey, entry.EntityState != EntityState.Unchanged);
                            }
                        }
                    }
                }

                // If the entity was previously referenced while it was still untracked, go back and do the fixup
                // that we would have done then now that the entity is tracked.
                foreach (var danglerEntry in stateManager.GetRecordedReferers(entry.Entity))
                {
                    DelayedFixup(danglerEntry.Item2, danglerEntry.Item1, entry);
                }

                // Esnure current value is snapshotted for all keys and FKs
                foreach (var property in entityType.GetProperties().Where(p => p.GetRelationshipIndex() >= 0))
                {
                    entry.SetRelationshipSnapshotValue(property, entry[property]);
                }
            }
        }
Ejemplo n.º 18
0
        /// <summary>
        ///     This API supports the Entity Framework Core infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public virtual void KeyPropertyChanged(
            InternalEntityEntry entry,
            IProperty property,
            IReadOnlyList <IKey> containingPrincipalKeys,
            IReadOnlyList <IForeignKey> containingForeignKeys,
            object oldValue,
            object newValue)
        {
            if (entry.EntityState == EntityState.Detached)
            {
                return;
            }

            try
            {
                _inFixup = true;

                var stateManager = entry.StateManager;

                foreach (var foreignKey in containingForeignKeys)
                {
                    var newPrincipalEntry = stateManager.GetPrincipal(entry, foreignKey);
                    var oldPrincipalEntry = stateManager.GetPrincipalUsingRelationshipSnapshot(entry, foreignKey);

                    var principalToDependent = foreignKey.PrincipalToDependent;
                    if (principalToDependent != null)
                    {
                        var collectionAccessor = principalToDependent.IsCollection() ? principalToDependent.GetCollectionAccessor() : null;

                        if (oldPrincipalEntry != null)
                        {
                            // Remove this entity from the principal collection that it was previously part of,
                            // or null the navigation for a 1:1 unless that reference was already changed.
                            if (collectionAccessor != null)
                            {
                                RemoveFromCollection(oldPrincipalEntry, principalToDependent, collectionAccessor, entry.Entity);
                            }
                            else if (ReferenceEquals(oldPrincipalEntry[principalToDependent], entry.Entity))
                            {
                                SetNavigation(oldPrincipalEntry, principalToDependent, null);
                            }
                        }

                        if (newPrincipalEntry != null)
                        {
                            // Add this entity to the collection of the new principal, or set the navigation for a 1:1
                            SetReferenceOrAddToCollection(newPrincipalEntry, principalToDependent, collectionAccessor, entry.Entity);
                        }
                    }

                    var dependentToPrincipal = foreignKey.DependentToPrincipal;
                    if (dependentToPrincipal != null)
                    {
                        if (newPrincipalEntry != null)
                        {
                            if (foreignKey.IsUnique)
                            {
                                // Dependent has been changed to point to a new principal.
                                // Find the dependent that previously pointed to the new principal and null out its FKs
                                // and navigation property. A.k.a. reference stealing.
                                // However, if the FK has already been changed or the reference is already set to point
                                // to something else, then don't change it.
                                var targetDependentEntry
                                    = stateManager.GetDependentsUsingRelationshipSnapshot(newPrincipalEntry, foreignKey).FirstOrDefault();

                                if (targetDependentEntry != null &&
                                    targetDependentEntry != entry)
                                {
                                    ConditionallyNullForeignKeyProperties(targetDependentEntry, newPrincipalEntry, foreignKey);

                                    if (ReferenceEquals(targetDependentEntry[dependentToPrincipal], newPrincipalEntry.Entity))
                                    {
                                        SetNavigation(targetDependentEntry, dependentToPrincipal, null);
                                    }
                                }
                            }

                            SetNavigation(entry, dependentToPrincipal, newPrincipalEntry.Entity);
                        }
                        else if (oldPrincipalEntry != null &&
                                 ReferenceEquals(entry[dependentToPrincipal], oldPrincipalEntry.Entity))
                        {
                            SetNavigation(entry, dependentToPrincipal, null);
                        }
                    }

                    stateManager.UpdateDependentMap(entry, foreignKey);
                }

                foreach (var key in containingPrincipalKeys)
                {
                    stateManager.UpdateIdentityMap(entry, key);

                    // Propagate principal key values into FKs
                    foreach (var foreignKey in key.GetReferencingForeignKeys())
                    {
                        foreach (var dependentEntry in stateManager.GetDependentsUsingRelationshipSnapshot(entry, foreignKey).ToList())
                        {
                            SetForeignKeyProperties(dependentEntry, entry, foreignKey, setModified: true);
                        }
                    }
                }

                entry.SetRelationshipSnapshotValue(property, newValue);
            }
            finally
            {
                _inFixup = false;
            }
        }
Ejemplo n.º 19
0
        private void DetectNavigationChange(InternalEntityEntry entry, INavigation navigation)
        {
            var snapshotValue = entry.GetRelationshipSnapshotValue(navigation);
            var currentValue  = entry[navigation];
            var stateManager  = entry.StateManager;

            var added = new HashSet <object>(ReferenceEqualityComparer.Instance);

            if (navigation.IsCollection())
            {
                var snapshotCollection = (IEnumerable)snapshotValue;
                var currentCollection  = (IEnumerable)currentValue;

                var removed = new HashSet <object>(ReferenceEqualityComparer.Instance);
                if (snapshotCollection != null)
                {
                    foreach (var entity in snapshotCollection)
                    {
                        removed.Add(entity);
                    }
                }

                if (currentCollection != null)
                {
                    foreach (var entity in currentCollection)
                    {
                        if (!removed.Remove(entity))
                        {
                            added.Add(entity);
                        }
                    }
                }

                if (added.Any() ||
                    removed.Any())
                {
                    stateManager.Notify.NavigationCollectionChanged(entry, navigation, added, removed);

                    foreach (var addedEntity in added)
                    {
                        entry.AddToCollectionSnapshot(navigation, addedEntity);
                    }

                    foreach (var removedEntity in removed)
                    {
                        entry.RemoveFromCollectionSnapshot(navigation, removedEntity);
                    }
                }
            }
            else if (!ReferenceEquals(currentValue, snapshotValue))
            {
                stateManager.Notify.NavigationReferenceChanged(entry, navigation, snapshotValue, currentValue);

                if (currentValue != null)
                {
                    added.Add(currentValue);
                }

                entry.SetRelationshipSnapshotValue(navigation, currentValue);
            }

            foreach (var addedEntity in added)
            {
                var addedEntry = stateManager.GetOrCreateEntry(addedEntity);
                if (addedEntry.EntityState == EntityState.Detached)
                {
                    _attacher.AttachGraph(addedEntry, EntityState.Added);
                }
            }
        }