예제 #1
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 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());
        }
예제 #2
0
        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);
            }
        }
예제 #4
0
        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() },
                    });
                }
            }
        }
예제 #6
0
 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;
 }
예제 #7
0
        public static RelationshipMultiplicity GetSourceMultiplicity(this INavigation navigation)
        {
            INavigation inverse = navigation.FindInverse();

            if (inverse == null)
            {
                return(RelationshipMultiplicity.One);
            }

            return(inverse.GetTargetMultiplicity());
        }
예제 #8
0
 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());
                }
            }
        }
예제 #11
0
        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());
                }
            }
        }
예제 #12
0
        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() },
                    });
                }
            }
        }
예제 #14
0
        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());
                }
            }
        }
예제 #15
0
        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);
            }
        }
예제 #17
0
        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);
            }
        }
예제 #19
0
        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);
            }
        }
예제 #20
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 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);
            }
        }
예제 #21
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 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);
            }
        }
예제 #22
0
        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]);
                }
            }
        }
예제 #23
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);
            }
        }
예제 #24
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 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);
            }
        }
예제 #25
0
        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);
            }
        }
예제 #26
0
        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);
            }
        }
예제 #27
0
        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);
            }
        }
예제 #28
0
        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]);
                }
            }
        }
예제 #29
0
        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);
            }
        }
예제 #30
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 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());
        }