internal override bool CheckIfNavigationPropertyContainsEntity(IEntityWrapper wrapper) { Debug.Assert(RelationshipNavigation != null, "null RelationshipNavigation"); // If the navigation property doesn't exist (e.g. unidirectional prop), then it can't contain the entity. if (!TargetAccessor.HasProperty) { return(false); } var value = WrappedOwner.GetNavigationPropertyValue(this); if (value != null) { var enumerable = value as IEnumerable; if (enumerable == null) { throw new EntityException( Strings.ObjectStateEntry_UnableToEnumerateCollection( TargetAccessor.PropertyName, WrappedOwner.Entity.GetType().FullName)); } foreach (var o in enumerable) { if (Equals(o, wrapper.Entity)) { return(true); } } } return(false); }
internal override bool CheckIfNavigationPropertyContainsEntity(IEntityWrapper wrapper) { Debug.Assert(RelationshipNavigation != null, "null RelationshipNavigation"); // If the navigation property doesn't exist (e.g. unidirectional prop), then it can't contain the entity. if (!TargetAccessor.HasProperty) { return(false); } var loadingState = DisableLazyLoading(); try { var value = WrappedOwner.GetNavigationPropertyValue(this); if (value != null) { // It would be good to be able to always use ICollection<T>.Contains here. The problem // is if the entity has overridden Equals/GetHashcode such that it makes use of the // primary key value then this will break when an Added object with an Identity key that // is contained in a navigation collection has its primary key set after it is saved. // Therefore, we only use this optimization if we know for sure that the nav prop is // using reference equality or if neither Equals or GetHashCode are overridden. // // Also, note that for most EF code to work the navigation property must be an ICollection. // However, some limited code paths work with IEnumerable, so we check for IEnumerable here // instead of ICollection to avoid breaking those code paths. If it's not IEnumerable, then // the message still tells people to use ICollection since pointing them to use IEnumerable // will likely cause more confusion and other errors as they continue development. var enumerable = value as IEnumerable <TEntity>; if (enumerable == null) { throw new EntityException( Strings.ObjectStateEntry_UnableToEnumerateCollection( TargetAccessor.PropertyName, WrappedOwner.Entity.GetType().FullName)); } var hashSet = value as HashSet <TEntity>; if (!wrapper.OverridesEqualsOrGetHashCode || (hashSet != null && hashSet.Comparer is ObjectReferenceEqualityComparer)) { // Contains extension method will short-circuit to ICollection.Contains if possible return(enumerable.Contains((TEntity)wrapper.Entity)); } return(enumerable.Any(o => ReferenceEquals(o, wrapper.Entity))); } } finally { ResetLazyLoading(loadingState); } return(false); }
internal override void AddToObjectCache(IEntityWrapper wrappedEntity) { DebugCheck.NotNull(wrappedEntity); // For POCO entities - add the object to the CLR collection if (TargetAccessor.HasProperty) // Null if the navigation does not exist in this direction { WrappedOwner.CollectionAdd(this, wrappedEntity.Entity); } }
internal override void AddToObjectCache(IEntityWrapper wrappedEntity) { DebugCheck.NotNull(wrappedEntity); // For POCO entities - set the CLR reference if (TargetAccessor.HasProperty) { WrappedOwner.SetNavigationPropertyValue(this, wrappedEntity.Entity); } }
internal override void VerifyNavigationPropertyForAdd(IEntityWrapper wrapper) { if (this.TargetAccessor.HasProperty) { object value = WrappedOwner.GetNavigationPropertyValue(this); if (!Object.ReferenceEquals(null, value) && !Object.Equals(value, wrapper.Entity)) { throw EntityUtil.CannotAddMoreThanOneEntityToEntityReference( this.RelationshipNavigation.To, this.RelationshipNavigation.RelationshipName); } } }
// <summary> // Remove from the POCO collection // </summary> internal override bool RemoveFromObjectCache(IEntityWrapper wrappedEntity) { DebugCheck.NotNull(wrappedEntity); // For POCO entities - remove the object from the CLR collection if (TargetAccessor.HasProperty) // Null if the navigation does not exist in this direction { return(WrappedOwner.CollectionRemove(this, wrappedEntity.Entity)); } return(false); }
// <summary> // Remove from the POCO collection // </summary> internal override bool RemoveFromObjectCache(IEntityWrapper wrappedEntity) { DebugCheck.NotNull(wrappedEntity); // For POCO entities - clear the CLR reference if (TargetAccessor.HasProperty) { WrappedOwner.RemoveNavigationPropertyValue(this, wrappedEntity.Entity); } return(true); }
internal override void VerifyNavigationPropertyForAdd(IEntityWrapper wrapper) { if (TargetAccessor.HasProperty) { var value = WrappedOwner.GetNavigationPropertyValue(this); if (!ReferenceEquals(null, value) && !ReferenceEquals(value, wrapper.Entity)) { throw new InvalidOperationException( Strings.EntityReference_CannotAddMoreThanOneEntityToEntityReference( RelationshipNavigation.To, RelationshipNavigation.RelationshipName)); } } }
internal override bool CheckIfNavigationPropertyContainsEntity(IEntityWrapper wrapper) { Debug.Assert(RelationshipNavigation != null, "null RelationshipNavigation"); // If the navigation property doesn't exist (e.g. unidirectional prop), then it can't contain the entity. if (!TargetAccessor.HasProperty) { return(false); } var value = WrappedOwner.GetNavigationPropertyValue(this); return(ReferenceEquals(value, wrapper.Entity)); }
internal override bool CheckIfNavigationPropertyContainsEntity(IEntityWrapper wrapper) { Debug.Assert(RelationshipNavigation != null, "null RelationshipNavigation"); // If the navigation property doesn't exist (e.g. unidirectional prop), then it can't contain the entity. if (!TargetAccessor.HasProperty) { return(false); } var value = WrappedOwner.GetNavigationPropertyValue(this); if (value != null) { // It would be good to be able to always use ICollection<T>.Contains here. The problem // is if the entity has overridden Equals/GetHashcode such that it makes use of the // primary key value then this will break when an Added object with an Identity key that // is contained in a navigation collection has its primary key set after it is saved. // Therefore, we only use this optimization if we know for sure that the nav prop is // using reference equality or if neither Equals or GetHashCode are overridden. var collection = value as ICollection <TEntity>; if (collection == null) { throw new EntityException( Strings.ObjectStateEntry_UnableToEnumerateCollection( TargetAccessor.PropertyName, WrappedOwner.Entity.GetType().FullName)); } var hashSet = value as HashSet <TEntity>; if (!wrapper.OverridesEqualsOrGetHashCode || (hashSet != null && hashSet.Comparer is ObjectReferenceEqualityComparer)) { return(collection.Contains((TEntity)wrapper.Entity)); } return(collection.Any(o => ReferenceEquals(o, wrapper.Entity))); } return(false); }
/// <summary> /// Attempts to null all FKs associated with the dependent end of this relationship on this entity. /// This may result in setting conceptual nulls if the FK is not nullable. /// </summary> internal void NullAllForeignKeys() { Debug.Assert(ObjectContext != null, "Nulling FKs only works when attached."); Debug.Assert(IsForeignKey, "Cannot null FKs for independent associations."); ObjectStateManager stateManager = ObjectContext.ObjectStateManager; EntityEntry entry = WrappedOwner.ObjectStateEntry; TransactionManager transManager = stateManager.TransactionManager; if (!transManager.IsGraphUpdate && !transManager.IsAttachTracking && !transManager.IsRelatedEndAdd) { ReferentialConstraint constraint = ((AssociationType)RelationMetadata).ReferentialConstraints.Single(); if (TargetRoleName == constraint.FromRole.Name) // Only do this on the dependent end { if (transManager.IsDetaching) { // If the principal is being detached, then the dependent must be added back to the // dangling keys index. // Perf note: The dependent currently gets added when it is being detached and is then // removed again later in the process. The code could be optimized to prevent this. Debug.Assert(entry != null, "State entry must exist while detaching."); EntityKey foreignKey = ForeignKeyFactory.CreateKeyFromForeignKeyValues(entry, this); if (foreignKey != null) { stateManager.AddEntryContainingForeignKeyToIndex(foreignKey, entry); } } else if (!ReferenceEquals(stateManager.EntityInvokingFKSetter, WrappedOwner.Entity) && !transManager.IsForeignKeyUpdate) { transManager.BeginForeignKeyUpdate(this); try { bool unableToNull = true; bool canSetModifiedProps = entry != null && (entry.State == EntityState.Modified || entry.State == EntityState.Unchanged); EntitySet dependentEntitySet = ((AssociationSet)RelationshipSet).AssociationSetEnds[FromEndProperty.Name].EntitySet; StateManagerTypeMetadata dependentTypeMetadata = stateManager.GetOrAddStateManagerTypeMetadata(WrappedOwner.IdentityType, dependentEntitySet); for (int i = 0; i < constraint.FromProperties.Count; i++) { string propertyName = constraint.ToProperties[i].Name; int dependentOrdinal = dependentTypeMetadata.GetOrdinalforOLayerMemberName(propertyName); StateManagerMemberMetadata member = dependentTypeMetadata.Member(dependentOrdinal); // This is a check for nullability in o-space. However, o-space nullability is not the // same as nullability of the underlying type. In particular, one difference is that when // attribute-based mapping is used then a property can be marked as not nullable in o-space // even when the underlying CLR type is nullable. For such a case, we treat the property // as if it were not nullable (since that's what we have shipped) even though we could // technically set it to null. if (member.ClrMetadata.Nullable) { // Only set the value to null if it is not already null. if (member.GetValue(WrappedOwner.Entity) != null) { WrappedOwner.SetCurrentValue( WrappedOwner.ObjectStateEntry, dependentTypeMetadata.Member(dependentOrdinal), -1, WrappedOwner.Entity, null); } else { // Given that the current value is null, this next check confirms that the original // value is also null. If it isn't, then we must make sure that the entity is marked // as modified. // This case can happen because fixup in the entity can set the FK to null while processing // a RelatedEnd operation. This will be detected by DetectChanges, but when performing // RelatedEnd operations the user is not required to call DetectChanges. if (canSetModifiedProps && WrappedOwner.ObjectStateEntry.OriginalValues.GetValue(dependentOrdinal) != null) { entry.SetModifiedProperty(propertyName); } } unableToNull = false; } else if (canSetModifiedProps) { entry.SetModifiedProperty(propertyName); } } if (unableToNull) { // We were unable to null out the FK because all FK properties were non-nullable. // We need to keep track of this state so that we treat the FK as null even though // we were not able to null it. This prevents the FK from being used for fixup and // also causes an exception to be thrown if an attempt is made to commit in this state. //We should only set a conceptual null if the entity is tracked if (entry != null) { //The CachedForeignKey may be null if we are putting //back a Conceptual Null as part of roll back EntityKey realKey = CachedForeignKey; if (realKey == null) { realKey = ForeignKeyFactory.CreateKeyFromForeignKeyValues(entry, this); } // Note that the realKey can still be null here for a situation where the key is marked not nullable // in o-space and yet the underlying type is nullable and the entity has been added or attached with a null // value for the property. This will cause SaveChanges to throw unless the entity is marked // as deleted before SaveChanges is called, in which case we don't want to set a conceptual // null here as the call might very well succeed in the database since, unless the FK is // a concurrency token, the value we have for it is not used at all for the delete. if (realKey != null) { SetCachedForeignKey(ForeignKeyFactory.CreateConceptualNullKey(realKey), entry); stateManager.RememberEntryWithConceptualNull(entry); } } } else { SetCachedForeignKey(null, entry); } } finally { transManager.EndForeignKeyUpdate(); } } } } }
internal bool NavigationPropertyIsNullOrMissing() { Debug.Assert(RelationshipNavigation != null, "null RelationshipNavigation"); return(!TargetAccessor.HasProperty || WrappedOwner.GetNavigationPropertyValue(this) == null); }