#pragma warning disable EF1001 // Internal EF Core API usage. // Issue #16707 private IUpdateEntry GetRootDocument(InternalEntityEntry entry) { var stateManager = entry.StateManager; var ownership = entry.EntityType.FindOwnership(); var principal = stateManager.FindPrincipal(entry, ownership); if (principal == null) { if (_sensitiveLoggingEnabled) { throw new InvalidOperationException( CosmosStrings.OrphanedNestedDocumentSensitive( entry.EntityType.DisplayName(), ownership.PrincipalEntityType.DisplayName(), entry.BuildCurrentValuesString(entry.EntityType.FindPrimaryKey().Properties))); } throw new InvalidOperationException( CosmosStrings.OrphanedNestedDocument( entry.EntityType.DisplayName(), ownership.PrincipalEntityType.DisplayName())); } return(principal.EntityType.IsDocumentRoot() ? principal : GetRootDocument(principal)); }
private void InitialFixup( InternalEntityEntry entry, ISet <IForeignKey> handledForeignKeys, bool fromQuery) { var entityType = (EntityType)entry.EntityType; var stateManager = entry.StateManager; IForeignKey conflictingPrincipalForeignKey = null; var matchingPrincipal = false; foreach (var foreignKey in entityType.GetForeignKeys()) { if (handledForeignKeys?.Contains(foreignKey) != true) { var principalEntry = stateManager.GetPrincipal(entry, foreignKey); if (principalEntry != null) { if (!foreignKey.PrincipalEntityType.IsAssignableFrom(principalEntry.EntityType)) { conflictingPrincipalForeignKey = foreignKey; } else { matchingPrincipal = true; // Set navigation to principal based on FK properties SetNavigation(entry, foreignKey.DependentToPrincipal, principalEntry); // Add this entity to principal's collection, or set inverse for 1:1 ToDependentFixup(entry, principalEntry, foreignKey); } } } } if (!matchingPrincipal && conflictingPrincipalForeignKey != null) { var principalEntry = stateManager.GetPrincipal(entry, conflictingPrincipalForeignKey); if (_sensitiveLoggingEnabled) { throw new InvalidOperationException( CoreStrings.IncompatiblePrincipalEntrySensitive( entry.BuildCurrentValuesString(conflictingPrincipalForeignKey.Properties), entityType.DisplayName(), entry.BuildOriginalValuesString(entityType.FindPrimaryKey().Properties), principalEntry.EntityType.DisplayName(), conflictingPrincipalForeignKey.PrincipalEntityType.DisplayName())); } throw new InvalidOperationException( CoreStrings.IncompatiblePrincipalEntry( Property.Format(conflictingPrincipalForeignKey.Properties), entityType.DisplayName(), principalEntry.EntityType.DisplayName(), conflictingPrincipalForeignKey.PrincipalEntityType.DisplayName())); } foreach (var foreignKey in entityType.GetReferencingForeignKeys()) { if (!foreignKey.DeclaringEntityType.IsQueryType && handledForeignKeys?.Contains(foreignKey) != true) { var dependents = stateManager.GetDependents(entry, foreignKey); if (foreignKey.IsUnique) { var dependentEntry = dependents.FirstOrDefault(); if (dependentEntry != null) { // Set navigations to and from principal entity that is indicated by FK SetNavigation(entry, foreignKey.PrincipalToDependent, dependentEntry); SetNavigation(dependentEntry, foreignKey.DependentToPrincipal, entry); } } else { foreach (var dependentEntry in dependents) { // Add to collection on principal indicated by FK and set inverse navigation AddToCollection(entry, foreignKey.PrincipalToDependent, dependentEntry); SetNavigation(dependentEntry, foreignKey.DependentToPrincipal, entry); } } } } // 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) { var setModified = entry.EntityState != EntityState.Unchanged && entry.EntityState != EntityState.Modified; foreach (var foreignKey in entityType.GetReferencingForeignKeys()) { if (!foreignKey.DeclaringEntityType.IsQueryType) { var principalToDependent = foreignKey.PrincipalToDependent; if (principalToDependent != null) { var navigationValue = entry[principalToDependent]; if (navigationValue != null) { if (principalToDependent.IsCollection()) { var dependents = ((IEnumerable)navigationValue).Cast <object>().ToList(); foreach (var dependentEntity in dependents) { var dependentEntry = stateManager.TryGetEntry(dependentEntity, foreignKey.DeclaringEntityType); 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 targetEntityType = principalToDependent.GetTargetType(); var dependentEntry = stateManager.TryGetEntry(navigationValue, targetEntityType); 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 targetEntityType = dependentToPrincipal.GetTargetType(); var principalEntry = stateManager.TryGetEntry(navigationValue, targetEntityType); 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, setModified); } } } } // 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.GetRecordedReferrers(entry.Entity, clear: true)) { DelayedFixup(danglerEntry.Item2, danglerEntry.Item1, entry); } } }