/// <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 static string ToDebugString([NotNull] this INavigation navigation, bool singleLine = true, [NotNull] string indent = "") { var builder = new StringBuilder(); builder.Append(indent); if (singleLine) { builder.Append("Navigation: ").Append(navigation.DeclaringEntityType.DisplayName()).Append("."); } builder.Append(navigation.Name); if (navigation.GetFieldName() == null) { builder.Append(" (no field, "); } else { builder.Append(" (").Append(navigation.GetFieldName()).Append(", "); } builder.Append(navigation.ClrType?.ShortDisplayName()).Append(")"); if (navigation.IsCollection()) { builder.Append(" Collection"); } builder.Append(navigation.IsDependentToPrincipal() ? " ToPrincipal " : " ToDependent "); builder.Append(navigation.GetTargetType().DisplayName()); if (navigation.FindInverse() != null) { builder.Append(" Inverse: ").Append(navigation.FindInverse().Name); } if (navigation.GetPropertyAccessMode() != PropertyAccessMode.PreferField) { builder.Append(" PropertyAccessMode.").Append(navigation.GetPropertyAccessMode()); } var indexes = navigation.GetPropertyIndexes(); builder.Append(" ").Append(indexes.Index); builder.Append(" ").Append(indexes.OriginalValueIndex); builder.Append(" ").Append(indexes.RelationshipIndex); builder.Append(" ").Append(indexes.ShadowIndex); builder.Append(" ").Append(indexes.StoreGenerationIndex); if (!singleLine) { builder.Append(navigation.AnnotationsToDebugString(indent + " ")); } return(builder.ToString()); }
private MetaNavProperty CreateNavProperty(INavigation p) { var np = new MetaNavProperty(); np.NameOnServer = p.Name; np.EntityTypeName = NormalizeTypeName(p.GetTargetType().ClrType); np.IsScalar = !p.IsCollection(); // FK_<dependent type name>_<principal type name>_<foreign key property name> np.AssociationName = BuildAssocName(p); if (p.IsDependentToPrincipal()) { np.AssociationName = BuildAssocName(p); np.ForeignKeyNamesOnServer = p.ForeignKey.Properties.Select(fkp => fkp.Name).ToList(); } else { var invP = p.FindInverse(); string assocName; if (invP == null) { assocName = "Inv_" + BuildAssocName(p); } else { assocName = BuildAssocName(invP); } np.AssociationName = assocName; np.InvForeignKeyNamesOnServer = p.ForeignKey.Properties.Select(fkp => fkp.Name).ToList(); } return(np); }
private static void RemoveDenormalizedInstances( IStateManager stateManager, INavigation navigation, object entity, object instance) { InternalEntityEntry internalEntityEntry = stateManager.TryGetEntry(entity); internalEntityEntry.EnsureRelationshipSnapshot(); INavigation inverse = navigation.FindInverse(); IKey primaryKey = inverse.DeclaringEntityType.FindPrimaryKey(); IProperty primaryKeyProperty = primaryKey.Properties.Single(); object keyValue = primaryKeyProperty.GetGetter().GetClrValue(instance); IClrCollectionAccessor collectionAccessor = navigation.GetCollectionAccessor(); ICollection list = (ICollection)collectionAccessor.GetOrCreate(entity); IList <object> toRemove = list .OfType <object>() .Where(item => (Equals( keyValue, primaryKeyProperty.GetGetter().GetClrValue(item)) && !ReferenceEquals(instance, item))) .ToList(); foreach (object item in toRemove) { collectionAccessor.Remove(entity, item); internalEntityEntry.RemoveFromCollectionSnapshot(navigation, item); } }
private static void NavigationsDfs(INavigation initial, Stack <string> path, List <string> allPaths, HashSet <string> includedNavigations, int depth) { // We exclude inverse navigation properties. var inverseNavigation = initial.FindInverse(); if (inverseNavigation != null) { includedNavigations.Add(inverseNavigation.Name); } //Recursion termination condition var navigations = initial.GetTargetType().GetNavigations().ToList(); if (navigations == null || navigations.All(n => includedNavigations.Contains(n.Name)) || depth <= 0) { allPaths.Add(string.Join(".", path.Reverse())); return; } foreach (var navigation in navigations) { if (includedNavigations.Add(navigation.Name)) { path.Push(navigation.Name); NavigationsDfs(navigation, path, allPaths, includedNavigations, depth - 1); path.Pop(); } } }
private void GenerateInversePropertyAttribute(INavigation navigation) { if (navigation.ForeignKey.PrincipalKey.IsPrimaryKey()) { var inverseNavigation = navigation.FindInverse(); if (inverseNavigation != null) { var inversePropertyAttribute = new AttributeWriter(nameof(InversePropertyAttribute)); var propertyName = EntityTypeTransformationService.TransformNavPropertyName(inverseNavigation.Name, navigation.DeclaringType.Name); inversePropertyAttribute.AddParameter( !navigation.DeclaringEntityType.GetPropertiesAndNavigations().Any( m => m.Name == inverseNavigation.DeclaringEntityType.Name || EntityTypeTransformationService.TransformNavPropertyName(m.Name, navigation.GetTargetType().Name) == EntityTypeTransformationService.TransformNavPropertyName(inverseNavigation.DeclaringEntityType.Name, navigation.GetTargetType().Name)) ? $"nameof({EntityTypeTransformationService.TransformEntityName(inverseNavigation.DeclaringType.Name)}.{propertyName})" : CSharpHelper.Literal(propertyName)); NavPropertyAnnotations.Add(new Dictionary <string, object> { { "nav-property-annotation", inversePropertyAttribute.ToString() }, }); } } }
internal ComBoostEntityCollection(EntityEntry owner, IEntityContext <T> context, INavigation navigation, IQueryable <T> queryable, int count) { _Entry = owner; _Navigation = navigation; _Inverse = navigation.FindInverse(); _Context = context; InnerQueryable = queryable; Count = count; }
public static RelationshipMultiplicity GetSourceMultiplicity(this INavigation navigation) { INavigation inverse = navigation.FindInverse(); if (inverse == null) { return(RelationshipMultiplicity.One); } return(inverse.GetTargetMultiplicity()); }
public void Add(T item) { if (_Inverse.IsCollection()) { _Entry.GetInfrastructure().AddToCollectionSnapshot(_Navigation, item); } else { _Navigation.FindInverse().GetSetter().SetClrValue(item, _Entry.Entity); } Count++; }
private static void FixupNavigation(INavigation navigation, object entity, object cached) { var value = navigation.GetGetter().GetClrValue(entity); if (value == null) { return; } if (navigation.FieldInfo != null) { navigation.FieldInfo.SetValue(cached, value); } else { navigation.GetSetter().SetClrValue(cached, value); } var inverse = navigation.FindInverse(); if (inverse == null) { return; } if (inverse.IsCollection()) { var collection = inverse.GetCollectionAccessor(); collection.Add(value, cached); } else { var setter = inverse.GetSetter(); if (value is IEnumerable enumerable) { foreach (var item in enumerable) { setter.SetClrValue(item, cached); } } else { setter.SetClrValue(value, cached); } } }
private void GenerateInversePropertyAttribute(INavigation navigation) { if (navigation.ForeignKey.PrincipalKey.IsPrimaryKey()) { var inverseNavigation = navigation.FindInverse(); if (inverseNavigation != null) { var inversePropertyAttribute = new AttributeWriter(nameof(InversePropertyAttribute)); inversePropertyAttribute.AddParameter(CSharpUtilities.DelimitString(inverseNavigation.Name)); _sb.AppendLine(inversePropertyAttribute.ToString()); } } }
private void GenerateInversePropertyAttribute(INavigation navigation) { if (navigation.ForeignKey.PrincipalKey.IsPrimaryKey()) { var inverseNavigation = navigation.FindInverse(); if (inverseNavigation != null) { var inversePropertyAttribute = new AttributeWriter(nameof(InversePropertyAttribute)); inversePropertyAttribute.AddParameter(this.code.Literal(inverseNavigation.Name)); this.IndentedStringBuilder.AppendLine(inversePropertyAttribute.ToString()); } } }
private void GenerateInversePropertyAttribute(INavigation navigation) { if (navigation.ForeignKey.PrincipalKey.IsPrimaryKey()) { var inverseNavigation = navigation.FindInverse(); if (inverseNavigation != null) { var inversePropertyAttribute = new AttributeWriter(nameof(InversePropertyAttribute)); inversePropertyAttribute.AddParameter($"nameof({inverseNavigation.DeclaringEntityType.Name}.{inverseNavigation.Name})"); _sb.AppendLine(inversePropertyAttribute.ToString()); } } }
private void GenerateInversePropertyAttribute(INavigation navigation) { if (navigation.ForeignKey.PrincipalKey.IsPrimaryKey()) { var inverseNavigation = navigation.FindInverse(); if (inverseNavigation != null) { var inversePropertyAttribute = new AttributeWriter(nameof(InversePropertyAttribute)); inversePropertyAttribute.AddParameter(CSharpHelper.Literal(inverseNavigation.Name)); NavPropertyAnnotations.Add(new Dictionary <string, object> { { "nav-property-annotation", inversePropertyAttribute.ToString() }, }); } } }
private void GenerateInversePropertyAttribute(INavigation navigation) { if (navigation.ForeignKey.PrincipalKey.IsPrimaryKey()) { var inverseNavigation = navigation.FindInverse(); if (inverseNavigation != null) { var inversePropertyAttribute = new AttributeWriter(nameof(InversePropertyAttribute)); inversePropertyAttribute.AddParameter( !navigation.DeclaringEntityType.GetPropertiesAndNavigations().Any( m => m.Name == inverseNavigation.DeclaringEntityType.Name) ? $"nameof({inverseNavigation.DeclaringEntityType.Name}.{inverseNavigation.Name})" : code.Literal(inverseNavigation.Name)); sb.AppendLine(inversePropertyAttribute.ToString()); } } }
private void SetInverse(InternalEntityEntry entry, INavigation navigation, object entity) { var inverse = navigation.FindInverse(); if (inverse != null) { var inverseEntry = entry.StateManager.GetOrCreateEntry(entity); if (inverse.IsCollection()) { var collectionAccessor = inverse.GetCollectionAccessor(); if (!collectionAccessor.Contains(entity, entry.Entity)) { collectionAccessor.Add(entity, entry.Entity); inverseEntry.AddToCollectionSnapshot(inverse, entry.Entity); } } else { var oldEntity = inverse.GetGetter().GetClrValue(entity); if ((oldEntity != null) && (oldEntity != entry.Entity)) { var oldEntry = entry.StateManager.GetOrCreateEntry(oldEntity); if (navigation.IsDependentToPrincipal()) { Unfixup(navigation, inverseEntry, oldEntry); SetNullForeignKey(oldEntry, navigation.ForeignKey.Properties); } else { Unfixup(navigation, oldEntry, inverseEntry); } } inverse.GetSetter().SetClrValue(entity, entry.Entity); inverseEntry.SetRelationshipSnapshotValue(inverse, entry.Entity); } } }
private void SetInverse(InternalEntityEntry entry, INavigation navigation, object entity) { var inverse = navigation.FindInverse(); if (inverse != null) { var inverseEntry = entry.StateManager.GetOrCreateEntry(entity); if (inverse.IsCollection()) { var collectionAccessor = _collectionAccessorSource.GetAccessor(inverse); if (!collectionAccessor.Contains(entity, entry.Entity)) { collectionAccessor.Add(entity, entry.Entity); } } else { var oldEntity = _getterSource.GetAccessor(inverse).GetClrValue(entity); if (oldEntity != null && oldEntity != entry.Entity) { var oldEntry = entry.StateManager.GetOrCreateEntry(oldEntity); if (navigation.PointsToPrincipal) { Unfixup(navigation, inverseEntry, oldEntry); SetNullForeignKey(oldEntry, navigation.ForeignKey.Properties); } else { Unfixup(navigation, oldEntry, inverseEntry); } } _setterSource.GetAccessor(inverse).SetClrValue(entity, entry.Entity); } inverseEntry.RelationshipsSnapshot.TakeSnapshot(inverse); } }
private static void ConditionallyClearInverse(InternalEntityEntry entry, INavigation navigation, object entity) { var inverse = navigation.FindInverse(); if (inverse != null) { if (inverse.IsCollection()) { inverse.GetCollectionAccessor().Remove(entity, entry.Entity); entry.StateManager.GetOrCreateEntry(entity).RemoveFromCollectionSnapshot(inverse, entry.Entity); } else { if (ReferenceEquals(inverse.GetGetter().GetClrValue(entity), entry.Entity)) { inverse.GetSetter().SetClrValue(entity, null); entry.StateManager.GetOrCreateEntry(entity).SetRelationshipSnapshotValue(inverse, null); } } } }
private void ConditionallyClearInverse(InternalEntityEntry entry, INavigation navigation, object entity) { var inverse = navigation.FindInverse(); if (inverse != null) { if (inverse.IsCollection()) { _collectionAccessorSource.GetAccessor(inverse).Remove(entity, entry.Entity); } else { if (ReferenceEquals(_getterSource.GetAccessor(inverse).GetClrValue(entity), entry.Entity)) { _setterSource.GetAccessor(inverse).SetClrValue(entity, null); } } entry.StateManager.GetOrCreateEntry(entity).RelationshipsSnapshot.TakeSnapshot(inverse); } }
private static void AddNavigationPropertyBindings( IModel efModel, INavigation navi, EdmEntityContainer container, IDictionary <IAnnotatable, IEdmElement> elementMap) { if (!navi.PointsToPrincipal()) { return; } var naviPair = new INavigation[] { navi, navi.FindInverse() }; for (var i = 0; i < 2; i++) { if (naviPair[i] == null) { continue; } var efEntityType = naviPair[i].DeclaringEntityType; if (!elementMap.ContainsKey(efEntityType)) { continue; } var entityType = elementMap[efEntityType] as IEdmEntityType; var navProperty = entityType.FindProperty(naviPair[i].Name) as IEdmNavigationProperty; if (navProperty == null) { continue; } var entitySet = (EdmEntitySet)container.EntitySets() .First(e => e.EntityType() == entityType); var targetEntitySet = container.EntitySets() .First(e => e.EntityType() == navProperty.ToEntityType()); entitySet.AddNavigationTarget(navProperty, targetEntitySet); } }
/// <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 NavigationCollectionChanged( InternalEntityEntry entry, INavigation navigation, IEnumerable <object> added, IEnumerable <object> removed) { if (_inFixup) { return; } var foreignKey = navigation.ForeignKey; var stateManager = entry.StateManager; var inverse = navigation.FindInverse(); foreach (var oldValue in removed) { var oldTargetEntry = stateManager.TryGetEntry(oldValue); if (oldTargetEntry != null && oldTargetEntry.EntityState != EntityState.Detached) { try { _inFixup = true; // Null FKs and navigations of dependents that have been removed, unless they // have already been changed. ConditionallyNullForeignKeyProperties(oldTargetEntry, entry, foreignKey); if (inverse != null && ReferenceEquals(oldTargetEntry[inverse], entry.Entity) && oldTargetEntry.StateManager.TryGetEntry(oldTargetEntry.Entity, throwOnNonUniqueness: false) != null) { SetNavigation(oldTargetEntry, inverse, null); } entry.RemoveFromCollectionSnapshot(navigation, oldValue); } finally { _inFixup = false; } } } foreach (var newValue in added) { var newTargetEntry = stateManager.GetOrCreateEntry(newValue); if (newTargetEntry.EntityState != EntityState.Detached) { try { _inFixup = true; // For a dependent added to the collection, remove it from the collection of // the principal entity that it was previously part of var oldPrincipalEntry = stateManager.GetPrincipalUsingRelationshipSnapshot(newTargetEntry, foreignKey); if (oldPrincipalEntry != null && oldPrincipalEntry != entry) { RemoveFromCollection(oldPrincipalEntry, navigation, newTargetEntry); } // Set the FK properties on added dependents to match this principal SetForeignKeyProperties(newTargetEntry, entry, foreignKey, setModified: true); // Set the inverse navigation to point to this principal SetNavigation(newTargetEntry, inverse, entry); } finally { _inFixup = false; } } else { stateManager.RecordReferencedUntrackedEntity(newValue, navigation, entry); _attacher.AttachGraph(newTargetEntry, EntityState.Added, forceStateWhenUnknownKey: false); } entry.AddToCollectionSnapshot(navigation, newValue); } }
/// <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 targetEntityType = navigation.GetTargetType(); 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.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) && victimDependentEntry.StateManager .TryGetEntry(victimDependentEntry.Entity, throwOnNonUniqueness: false) != null) { 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); 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); } // 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); } } } else { Debug.Assert(foreignKey.IsUnique); 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); } } 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); } } 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); var targetEntry = targetEntityType.HasDefiningNavigation() ? stateManager.GetOrCreateEntry(newValue, targetEntityType) : stateManager.GetOrCreateEntry(newValue); _attacher.AttachGraph(targetEntry, EntityState.Added, forceStateWhenUnknownKey: false); } }
private static void AddNavigationProperties( IModel efModel, INavigation navi, EdmModel model, IDictionary<IAnnotatable, IEdmElement> elementMap) { if (!navi.PointsToPrincipal()) { return; } var naviPair = new INavigation[] { navi, navi.FindInverse() }; var navPropertyInfos = new EdmNavigationPropertyInfo[2]; for (var i = 0; i < 2; i++) { var efEnd = naviPair[i]; if (efEnd == null) continue; var efEntityType = efEnd.DeclaringEntityType; if (!elementMap.ContainsKey(efEntityType)) { continue; } var entityType = elementMap[efEntityType] as IEdmEntityType; var efTargetEntityType = naviPair[i].GetTargetType(); if (!elementMap.ContainsKey(efTargetEntityType)) { continue; } var targetEntityType = elementMap[ efTargetEntityType] as IEdmEntityType; navPropertyInfos[i] = new EdmNavigationPropertyInfo() { ContainsTarget = false, Name = naviPair[i].Name, // TODO GitHubIssue#57: Complete EF7 to EDM model mapping //OnDelete = efEnd.DeleteBehavior == OperationAction.Cascade // ? EdmOnDeleteAction.Cascade : EdmOnDeleteAction.None, OnDelete = EdmOnDeleteAction.None, Target = targetEntityType, TargetMultiplicity = ModelProducer.GetEdmMultiplicity( naviPair[i]), }; var foreignKey = naviPair[i].ForeignKey; if (foreignKey != null && naviPair[i].PointsToPrincipal()) { navPropertyInfos[i].DependentProperties = foreignKey.Properties .Select(p => entityType.FindProperty(p.Name) as IEdmStructuralProperty); navPropertyInfos[i].PrincipalProperties = foreignKey.PrincipalKey.Properties .Select(p => targetEntityType.FindProperty(p.Name) as IEdmStructuralProperty); } } if (navPropertyInfos[0] == null && navPropertyInfos[1] != null) { var efEntityType = navi.GetTargetType(); var entityType = elementMap[efEntityType] as EdmEntityType; if (entityType.FindProperty(navPropertyInfos[1].Name) == null) { entityType.AddUnidirectionalNavigation(navPropertyInfos[1]); } } if (navPropertyInfos[0] != null && navPropertyInfos[1] == null) { var efEntityType = navi.DeclaringEntityType; var entityType = elementMap[efEntityType] as EdmEntityType; if (entityType.FindProperty(navPropertyInfos[0].Name) == null) { entityType.AddUnidirectionalNavigation(navPropertyInfos[0]); } } if (navPropertyInfos[0] != null && navPropertyInfos[1] != null) { var efEntityType = navi.DeclaringEntityType; var entityType = elementMap[efEntityType] as EdmEntityType; if (entityType.FindProperty(navPropertyInfos[0].Name) == null) { entityType.AddBidirectionalNavigation( navPropertyInfos[0], navPropertyInfos[1]); } } }
/// <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); } }
/// <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 NavigationCollectionChanged( InternalEntityEntry entry, INavigation navigation, IEnumerable<object> added, IEnumerable<object> removed) { if (_inFixup) { return; } var foreignKey = navigation.ForeignKey; var stateManager = entry.StateManager; var inverse = navigation.FindInverse(); var collectionAccessor = navigation.GetCollectionAccessor(); foreach (var oldValue in removed) { var oldTargetEntry = stateManager.TryGetEntry(oldValue); if (oldTargetEntry != null && oldTargetEntry.EntityState != EntityState.Detached) { try { _inFixup = true; // Null FKs and navigations of dependents that have been removed, unless they // have already been changed. ConditionallyNullForeignKeyProperties(oldTargetEntry, entry, foreignKey); if (inverse != null && ReferenceEquals(oldTargetEntry[inverse], entry.Entity)) { SetNavigation(oldTargetEntry, inverse, null); } entry.RemoveFromCollectionSnapshot(navigation, oldValue); } finally { _inFixup = false; } } } foreach (var newValue in added) { var newTargetEntry = stateManager.GetOrCreateEntry(newValue); if (newTargetEntry.EntityState != EntityState.Detached) { try { _inFixup = true; // For a dependent added to the collection, remove it from the collection of // the principal entity that it was previously part of var oldPrincipalEntry = stateManager.GetPrincipal(newTargetEntry, foreignKey); if (oldPrincipalEntry != null) { RemoveFromCollection(oldPrincipalEntry, navigation, collectionAccessor, newValue); } // Set the FK properties on added dependents to match this principal SetForeignKeyProperties(newTargetEntry, entry, foreignKey, setModified: true); // Set the inverse navigation to point to this principal SetNavigation(newTargetEntry, inverse, entry.Entity); } finally { _inFixup = false; } } else { stateManager.RecordReferencedUntrackedEntity(newValue, navigation, entry); _attacher.AttachGraph(newTargetEntry, EntityState.Added); } entry.AddToCollectionSnapshot(navigation, newValue); } }
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) { _attacher.AttachGraph(stateManager.GetOrCreateEntry(newValue), EntityState.Added); } }
private void SetInverse(InternalEntityEntry entry, INavigation navigation, object entity) { var inverse = navigation.FindInverse(); if (inverse != null) { var inverseEntry = entry.StateManager.GetOrCreateEntry(entity); if (inverse.IsCollection()) { var collectionAccessor = _collectionAccessorSource.GetAccessor(inverse); if (!collectionAccessor.Contains(entity, entry.Entity)) { collectionAccessor.Add(entity, entry.Entity); } } else { var oldEntity = _getterSource.GetAccessor(inverse).GetClrValue(entity); if (oldEntity != null && oldEntity != entry.Entity) { var oldEntry = entry.StateManager.GetOrCreateEntry(oldEntity); if (navigation.PointsToPrincipal()) { Unfixup(navigation, inverseEntry, oldEntry); SetNullForeignKey(oldEntry, navigation.ForeignKey.Properties); } else { Unfixup(navigation, oldEntry, inverseEntry); } } _setterSource.GetAccessor(inverse).SetClrValue(entity, entry.Entity); } inverseEntry.RelationshipsSnapshot.TakeSnapshot(inverse); } }
private static void AddNavigationProperties( IModel efModel, INavigation navigation, EdmModel model, IDictionary <IAnnotatable, IEdmElement> elementMap) { if (!navigation.PointsToPrincipal()) { return; } var naviPair = new INavigation[] { navigation, navigation.FindInverse() }; var navPropertyInfos = new EdmNavigationPropertyInfo[2]; for (var i = 0; i < 2; i++) { var navi = naviPair[i]; if (navi == null) { continue; } var efEntityType = navi.DeclaringEntityType; if (!elementMap.ContainsKey(efEntityType)) { continue; } var entityType = elementMap[efEntityType] as IEdmEntityType; var efTargetEntityType = navi.GetTargetType(); if (!elementMap.ContainsKey(efTargetEntityType)) { continue; } var targetEntityType = elementMap[efTargetEntityType] as IEdmEntityType; navPropertyInfos[i] = new EdmNavigationPropertyInfo() { ContainsTarget = false, Name = navi.Name, Target = targetEntityType, TargetMultiplicity = ModelProducer.GetEdmMultiplicity(navi), }; var foreignKey = navi.ForeignKey; if (foreignKey != null && navi.PointsToPrincipal()) { navPropertyInfos[i].OnDelete = foreignKey.DeleteBehavior == DeleteBehavior.Cascade ? EdmOnDeleteAction.Cascade : EdmOnDeleteAction.None; navPropertyInfos[i].DependentProperties = foreignKey.Properties .Select(p => entityType.FindProperty(p.Name) as IEdmStructuralProperty); navPropertyInfos[i].PrincipalProperties = foreignKey.PrincipalKey.Properties .Select(p => targetEntityType.FindProperty(p.Name) as IEdmStructuralProperty); } } if (navPropertyInfos[0] == null && navPropertyInfos[1] != null) { var efEntityType = navigation.GetTargetType(); var entityType = elementMap[efEntityType] as EdmEntityType; if (entityType.FindProperty(navPropertyInfos[1].Name) == null) { entityType.AddUnidirectionalNavigation(navPropertyInfos[1]); } } if (navPropertyInfos[0] != null && navPropertyInfos[1] == null) { var efEntityType = navigation.DeclaringEntityType; var entityType = elementMap[efEntityType] as EdmEntityType; if (entityType.FindProperty(navPropertyInfos[0].Name) == null) { entityType.AddUnidirectionalNavigation(navPropertyInfos[0]); } } if (navPropertyInfos[0] != null && navPropertyInfos[1] != null) { var efEntityType = navigation.DeclaringEntityType; var entityType = elementMap[efEntityType] as EdmEntityType; if (entityType.FindProperty(navPropertyInfos[0].Name) == null) { entityType.AddBidirectionalNavigation( navPropertyInfos[0], navPropertyInfos[1]); } } }
private static void AddNavigationPropertyBindings( IModel efModel, INavigation navi, EdmEntityContainer container, IDictionary<IAnnotatable, IEdmElement> elementMap) { if (!navi.PointsToPrincipal()) { return; } var naviPair = new INavigation[] { navi, navi.FindInverse() }; for (var i = 0; i < 2; i++) { if (naviPair[i] == null) continue; var efEntityType = naviPair[i].DeclaringEntityType; if (!elementMap.ContainsKey(efEntityType)) { continue; } var entityType = elementMap[efEntityType] as IEdmEntityType; var navProperty = entityType.FindProperty( naviPair[i].Name) as IEdmNavigationProperty; if (navProperty == null) { continue; } var entitySet = (EdmEntitySet)container.EntitySets(). First(e => e.EntityType() == entityType); var targetEntitySet = container.EntitySets(). First(e => e.EntityType() == navProperty.ToEntityType()); entitySet.AddNavigationTarget(navProperty, targetEntitySet); } }
/// <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 static string ToDebugString( [NotNull] this INavigation navigation, bool singleLine = true, bool includeIndexes = false, [NotNull] string indent = "", bool detailed = true) { var builder = new StringBuilder(); builder.Append(indent); if (singleLine) { builder.Append($"Navigation: {navigation.DeclaringEntityType.DisplayName()}."); } builder.Append(navigation.Name); if (!detailed) { return(builder.ToString()); } var field = navigation.GetFieldName(); if (field == null) { builder.Append(" (no field, "); } else if (!field.EndsWith(">k__BackingField", StringComparison.Ordinal)) { builder.Append($" ({field}, "); } else { builder.Append($" ("); } builder.Append(navigation.ClrType?.ShortDisplayName()).Append(")"); if (navigation.IsCollection()) { builder.Append(" Collection"); } builder.Append(navigation.IsDependentToPrincipal() ? " ToPrincipal " : " ToDependent "); builder.Append(navigation.GetTargetType().DisplayName()); if (navigation.FindInverse() != null) { builder.Append(" Inverse: ").Append(navigation.FindInverse().Name); } if (navigation.GetPropertyAccessMode() != PropertyAccessMode.PreferField) { builder.Append(" PropertyAccessMode.").Append(navigation.GetPropertyAccessMode()); } if (includeIndexes) { var indexes = navigation.GetPropertyIndexes(); builder.Append(" ").Append(indexes.Index); builder.Append(" ").Append(indexes.OriginalValueIndex); builder.Append(" ").Append(indexes.RelationshipIndex); builder.Append(" ").Append(indexes.ShadowIndex); builder.Append(" ").Append(indexes.StoreGenerationIndex); } if (!singleLine) { builder.Append(navigation.AnnotationsToDebugString(indent + " ")); } return(builder.ToString()); }