/// <summary> /// Gets the NavigationProperty that allows you to navigate from entity represented /// by the localEnd to the entity represented by the foreignEnd. /// </summary> private NavigationProperty getProperty(ObjectStateEntry entry, AssociationEndMember localEnd, AssociationEndMember foreignEnd, EntityKey key) { var relationshipType = entry.EntitySet.ElementType; var entitySet = key.GetEntitySet(entry.ObjectStateManager.MetadataWorkspace); return(entitySet.ElementType.NavigationProperties .Where(p => p.RelationshipType == relationshipType && p.FromEndMember == localEnd && p.ToEndMember == foreignEnd) .SingleOrDefault()); }
// Note that this is called only for association ends. Entities have key values inline. private PropagatorResult CreateEntityKeyResult(IEntityStateEntry stateEntry, EntityKey entityKey) { // get metadata for key var entityType = entityKey.GetEntitySet(m_translator.MetadataWorkspace).ElementType; var keyRowType = entityType.GetKeyRowType(); var keyMetadata = m_translator.GetExtractorMetadata(stateEntry.EntitySet, keyRowType); var keyMemberCount = keyRowType.Properties.Count; var keyValues = new PropagatorResult[keyMemberCount]; for (var ordinal = 0; ordinal < keyRowType.Properties.Count; ordinal++) { EdmMember keyMember = keyRowType.Properties[ordinal]; // retrieve information about this key value var keyMemberInformation = keyMetadata.m_memberMap[ordinal]; var keyIdentifier = m_translator.KeyManager.GetKeyIdentifierForMemberOffset(entityKey, ordinal, keyRowType.Properties.Count); object keyValue = null; if (entityKey.IsTemporary) { // If the EntityKey is temporary, we need to retrieve the appropriate // key value from the entity itself (or in this case, the IEntityStateEntry). var entityEntry = stateEntry.StateManager.GetEntityStateEntry(entityKey); Debug.Assert( entityEntry.State == EntityState.Added, "The corresponding entry for a temp EntityKey should be in the Added State."); keyValue = entityEntry.CurrentValues[keyMember.Name]; } else { // Otherwise, we extract the value from within the EntityKey. keyValue = entityKey.FindValueByName(keyMember.Name); } Debug.Assert(keyValue != null, "keyValue should've been retrieved."); // construct propagator result keyValues[ordinal] = PropagatorResult.CreateKeyValue( keyMemberInformation.Flags, keyValue, stateEntry, keyIdentifier); // see UpdateTranslator.Identifiers for information on key identifiers and ordinals } return(PropagatorResult.CreateStructuralValue(keyValues, keyMetadata.m_type, false)); }
internal override void ValidateDetachedEntityKey() { if (!this.IsEmpty() || !(this.DetachedEntityKey != (EntityKey)null)) { return; } EntityKey detachedEntityKey = this.DetachedEntityKey; if (!RelatedEnd.IsValidEntityKeyType(detachedEntityKey)) { throw Error.EntityReference_CannotSetSpecialKeys(); } EntitySet entitySet = detachedEntityKey.GetEntitySet(this.ObjectContext.MetadataWorkspace); this.CheckRelationEntitySet(entitySet); detachedEntityKey.ValidateEntityKey(this.ObjectContext.MetadataWorkspace, entitySet); }
private PropagatorResult CreateEntityKeyResult( IEntityStateEntry stateEntry, EntityKey entityKey) { RowType keyRowType = entityKey.GetEntitySet(this.m_translator.MetadataWorkspace).ElementType.GetKeyRowType(); ExtractorMetadata extractorMetadata = this.m_translator.GetExtractorMetadata(stateEntry.EntitySet, (StructuralType)keyRowType); PropagatorResult[] values = new PropagatorResult[keyRowType.Properties.Count]; for (int memberOffset = 0; memberOffset < keyRowType.Properties.Count; ++memberOffset) { EdmMember property = (EdmMember)keyRowType.Properties[memberOffset]; ExtractorMetadata.MemberInformation member = extractorMetadata.m_memberMap[memberOffset]; int identifierForMemberOffset = this.m_translator.KeyManager.GetKeyIdentifierForMemberOffset(entityKey, memberOffset, keyRowType.Properties.Count); object obj = !entityKey.IsTemporary ? entityKey.FindValueByName(property.Name) : stateEntry.StateManager.GetEntityStateEntry(entityKey).CurrentValues[property.Name]; values[memberOffset] = PropagatorResult.CreateKeyValue(member.Flags, obj, stateEntry, identifierForMemberOffset); } return(PropagatorResult.CreateStructuralValue(values, extractorMetadata.m_type, false)); }
public IEnumerable <T2> GetAssociationChanges <T1, T2>(T1 parent, string propertyName, EntityState findState) { ObjectContext ocontext = ((IObjectContextAdapter)this.context).ObjectContext; MetadataWorkspace metadataWorkspace = ocontext.MetadataWorkspace; // Find the AssociationType that matches the property traits given as input AssociationType atype = metadataWorkspace.GetItems <AssociationType>(DataSpace.CSpace) .Where(a => a.AssociationEndMembers.Any( ae => ae.MetadataProperties.Any(mp => mp.Name == "ClrPropertyInfo" && // Magic string!!! ((PropertyInfo)mp.Value).Name == propertyName && typeof(T1).IsAssignableFrom(((PropertyInfo)mp.Value).DeclaringType) ) ) ).First(); // Find added or deleted DbDataRecords from the above discovered type ocontext.DetectChanges(); IEnumerable <DbDataRecord> dbDataRecords = ocontext.ObjectStateManager .GetObjectStateEntries(findState) .Where(e => e.IsRelationship) // Oddly, this works, while doing the same thing below requires comparing .Name...? .Where(e => e.EntitySet.ElementType == atype) .Select(e => findState == EntityState.Deleted ? e.OriginalValues : e.CurrentValues); // Get the actual entities using the EntityKeys in the DbDataRecord IList <T2> relationChanges = new List <T2>(); foreach (System.Data.Common.DbDataRecord ddr in dbDataRecords) { EntityKey ek = (EntityKey)ddr[0]; // Comparing .ElementType to atype doesn't work, see above...? if (!(ek.GetEntitySet(metadataWorkspace).ElementType.Name == atype.Name)) { ek = (EntityKey)ddr[1]; } relationChanges.Add((T2)ocontext.GetObjectByKey(ek)); } return(relationChanges); }
internal void SetEntityKey(EntityKey value, bool forceFixup) { if (value != null && value == EntityKey && (ReferenceValue.Entity != null || (ReferenceValue.Entity == null && !forceFixup))) { // "no-op" -- this is not really no-op in the attached case, because at a minimum we have to do a key lookup, // worst case we have to review all relationships for the owner entity // However, if we don't do this, we can get into a scenario where we are setting the key to the same thing it's already set to // and this could have side effects, especially with RI constraints and cascade delete. We don't want to delete something // and then add it back, if that deleting could have additional unexpected effects. Don't bother doing this check if value is // null, because EntityKey could be null even if there are Added/Unchanged relationships, if the target entity has a temporary key. // In that case, we still need to delete that existing relationship, so it's not a no-op return; } if (ObjectContext != null && !UsingNoTracking) { Debug.Assert(WrappedOwner.Entity != null, "Unexpected null Owner on EntityReference attached to a context"); // null is a valid value for the EntityKey, but temporary and special keys are not // devnote: Can't check this on detached references because this property could be set to a temp key during deserialization, // if the key hasn't finished deserializing yet. if (value != null && !IsValidEntityKeyType(value)) { throw new ArgumentException(Strings.EntityReference_CannotSetSpecialKeys, "value"); } if (value == null) { if (AttemptToNullFKsOnRefOrKeySetToNull()) { DetachedEntityKey = null; } else { ReferenceValue = NullEntityWrapper.NullWrapper; } } else { // Verify that the key has the right EntitySet for this RelationshipSet var targetEntitySet = value.GetEntitySet(ObjectContext.MetadataWorkspace); CheckRelationEntitySet(targetEntitySet); value.ValidateEntityKey(ObjectContext.MetadataWorkspace, targetEntitySet, true /*isArgumentException */, "value"); var manager = ObjectContext.ObjectStateManager; // If we already have an entry with this key, we just need to create a relationship with it var addNewRelationship = false; // If we don't already have any matching entries for this key, we'll have to create a new entry var addKeyEntry = false; var targetEntry = manager.FindEntityEntry(value); if (targetEntry != null) { // If it's not a key entry, just use the entity to set this reference's Value if (!targetEntry.IsKeyEntry) { // Delegate to the Value property to clear any existing relationship // and to add the new one. This will fire the appropriate events and // ensure that the related ends are connected. // It has to be a TEntity since we already verified that the EntitySet is correct above ReferenceValue = targetEntry.WrappedEntity; } else { // if the existing entry is a key entry, we just need to // add a new relationship between the source entity and that key addNewRelationship = true; } } else { // no entry exists, so we'll need to add a key along with the relationship addKeyEntry = !IsForeignKey; addNewRelationship = true; } if (addNewRelationship) { var ownerKey = ValidateOwnerWithRIConstraints( targetEntry == null ? null : targetEntry.WrappedEntity, value, checkBothEnds: true); // Verify that the owner is in a valid state for adding a relationship ValidateStateForAdd(WrappedOwner); if (addKeyEntry) { manager.AddKeyEntry(value, targetEntitySet); } // First, clear any existing relationships manager.TransactionManager.EntityBeingReparented = WrappedOwner.Entity; try { ClearCollectionOrRef(null, null, /*doCascadeDelete*/ false); } finally { manager.TransactionManager.EntityBeingReparented = null; } // Then add the new one if (IsForeignKey) { DetachedEntityKey = value; // Update the FK values in this entity if (IsDependentEndOfReferentialConstraint(false)) { UpdateForeignKeyValues(WrappedOwner, value); } } else { var wrapper = new RelationshipWrapper( (AssociationSet)RelationshipSet, RelationshipNavigation.From, ownerKey, RelationshipNavigation.To, value); // Add the relationship in the unchanged state if var relationshipState = EntityState.Added; // If this is an unchanged/modified dependent end of a relationship and we are allowing the EntityKey to be set // create the relationship in the Unchanged state because the state must "match" the dependent end state if (!ownerKey.IsTemporary && IsDependentEndOfReferentialConstraint(false)) { relationshipState = EntityState.Unchanged; } manager.AddNewRelation(wrapper, relationshipState); } } } } else { // Just set the field for detached object -- during Attach/Add we will make sure this value // is not in conflict if the EntityReference contains a real entity. We cannot always determine the // EntityKey for any real entity in the detached state, so we don't bother to do it here. DetachedEntityKey = value; } }
internal void SetEntityKey(EntityKey value, bool forceFixup) { if (value != null && value == EntityKey && (ReferenceValue.Entity != null || (ReferenceValue.Entity == null && !forceFixup))) { // "no-op" -- this is not really no-op in the attached case, because at a minimum we have to do a key lookup, // worst case we have to review all relationships for the owner entity // However, if we don't do this, we can get into a scenario where we are setting the key to the same thing it's already set to // and this could have side effects, especially with RI constraints and cascade delete. We don't want to delete something // and then add it back, if that deleting could have additional unexpected effects. Don't bother doing this check if value is // null, because EntityKey could be null even if there are Added/Unchanged relationships, if the target entity has a temporary key. // In that case, we still need to delete that existing relationship, so it's not a no-op return; } if (this.ObjectContext != null && !UsingNoTracking) { Debug.Assert(this.WrappedOwner.Entity != null, "Unexpected null Owner on EntityReference attached to a context"); // null is a valid value for the EntityKey, but temporary and special keys are not // devnote: Can't check this on detached references because this property could be set to a temp key during deserialization, // if the key hasn't finished deserializing yet. if (value != null && !IsValidEntityKeyType(value)) { throw EntityUtil.CannotSetSpecialKeys(); } if (value == null) { if (AttemptToNullFKsOnRefOrKeySetToNull()) { DetachedEntityKey = null; } else { ReferenceValue = EntityWrapperFactory.NullWrapper; } } else { // Verify that the key has the right EntitySet for this RelationshipSet EntitySet targetEntitySet = value.GetEntitySet(ObjectContext.MetadataWorkspace); CheckRelationEntitySet(targetEntitySet); value.ValidateEntityKey(ObjectContext.MetadataWorkspace, targetEntitySet, true /*isArgumentException */, "value"); ObjectStateManager manager = this.ObjectContext.ObjectStateManager; // If we already have an entry with this key, we just need to create a relationship with it bool addNewRelationship = false; // If we don't already have any matching entries for this key, we'll have to create a new entry bool addKeyEntry = false; EntityEntry targetEntry = manager.FindEntityEntry(value); if (targetEntry != null) { // If it's not a key entry, just use the entity to set this reference's Value if (!targetEntry.IsKeyEntry) { // Delegate to the Value property to clear any existing relationship // and to add the new one. This will fire the appropriate events and // ensure that the related ends are connected. // It has to be a TEntity since we already verified that the EntitySet is correct above this.ReferenceValue = targetEntry.WrappedEntity; } else { // if the existing entry is a key entry, we just need to // add a new relationship between the source entity and that key addNewRelationship = true; } } else { // no entry exists, so we'll need to add a key along with the relationship addKeyEntry = !IsForeignKey; addNewRelationship = true; } if (addNewRelationship) { EntityKey ownerKey = ValidateOwnerWithRIConstraints(targetEntry == null ? null : targetEntry.WrappedEntity, value, checkBothEnds: true); // Verify that the owner is in a valid state for adding a relationship ValidateStateForAdd(this.WrappedOwner); if (addKeyEntry) { manager.AddKeyEntry(value, targetEntitySet); } // First, clear any existing relationships manager.TransactionManager.EntityBeingReparented = WrappedOwner.Entity; try { ClearCollectionOrRef(null, null, /*doCascadeDelete*/ false); } finally { manager.TransactionManager.EntityBeingReparented = null; } // Then add the new one if (IsForeignKey) { DetachedEntityKey = value; // Update the FK values in this entity if (IsDependentEndOfReferentialConstraint(false)) { UpdateForeignKeyValues(WrappedOwner, value); } } else { RelationshipWrapper wrapper = new RelationshipWrapper((AssociationSet)RelationshipSet, RelationshipNavigation.From, ownerKey, RelationshipNavigation.To, value); // Add the relationship in the unchanged state if EntityState relationshipState = EntityState.Added; // If this is an unchanged/modified dependent end of a relationship and we are allowing the EntityKey to be set // create the relationship in the Unchanged state because the state must "match" the dependent end state if (!ownerKey.IsTemporary && IsDependentEndOfReferentialConstraint(false)) { relationshipState = EntityState.Unchanged; } manager.AddNewRelation(wrapper, relationshipState); } } } } else { // Just set the field for detached object -- during Attach/Add we will make sure this value // is not in conflict if the EntityReference contains a real entity. We cannot always determine the // EntityKey for any real entity in the detached state, so we don't bother to do it here. DetachedEntityKey = value; } }
internal void SetEntityKey(EntityKey value, bool forceFixup) { if (value != (EntityKey)null && value == this.EntityKey && (this.ReferenceValue.Entity != null || this.ReferenceValue.Entity == null && !forceFixup)) { return; } if (this.ObjectContext != null && !this.UsingNoTracking) { if (value != (EntityKey)null && !RelatedEnd.IsValidEntityKeyType(value)) { throw new ArgumentException(Strings.EntityReference_CannotSetSpecialKeys, nameof(value)); } if (value == (EntityKey)null) { if (this.AttemptToNullFKsOnRefOrKeySetToNull()) { this.DetachedEntityKey = (EntityKey)null; } else { this.ReferenceValue = NullEntityWrapper.NullWrapper; } } else { EntitySet entitySet = value.GetEntitySet(this.ObjectContext.MetadataWorkspace); this.CheckRelationEntitySet(entitySet); value.ValidateEntityKey(this.ObjectContext.MetadataWorkspace, entitySet, true, nameof(value)); ObjectStateManager objectStateManager = this.ObjectContext.ObjectStateManager; bool flag1 = false; bool flag2 = false; EntityEntry entityEntry = objectStateManager.FindEntityEntry(value); if (entityEntry != null) { if (!entityEntry.IsKeyEntry) { this.ReferenceValue = entityEntry.WrappedEntity; } else { flag1 = true; } } else { flag2 = !this.IsForeignKey; flag1 = true; } if (!flag1) { return; } EntityKey key0 = this.ValidateOwnerWithRIConstraints(entityEntry == null ? (IEntityWrapper)null : entityEntry.WrappedEntity, value, true); this.ValidateStateForAdd(this.WrappedOwner); if (flag2) { objectStateManager.AddKeyEntry(value, entitySet); } objectStateManager.TransactionManager.EntityBeingReparented = this.WrappedOwner.Entity; try { this.ClearCollectionOrRef((IEntityWrapper)null, (RelationshipNavigation)null, false); } finally { objectStateManager.TransactionManager.EntityBeingReparented = (object)null; } if (this.IsForeignKey) { this.DetachedEntityKey = value; if (!this.IsDependentEndOfReferentialConstraint(false)) { return; } this.UpdateForeignKeyValues(this.WrappedOwner, value); } else { RelationshipWrapper wrapper = new RelationshipWrapper((AssociationSet)this.RelationshipSet, this.RelationshipNavigation.From, key0, this.RelationshipNavigation.To, value); EntityState desiredState = EntityState.Added; if (!key0.IsTemporary && this.IsDependentEndOfReferentialConstraint(false)) { desiredState = EntityState.Unchanged; } objectStateManager.AddNewRelation(wrapper, desiredState); } } } else { this.DetachedEntityKey = value; } }
// Note that this is called only for association ends. Entities have key values inline. private PropagatorResult CreateEntityKeyResult(IEntityStateEntry stateEntry, EntityKey entityKey) { // get metadata for key var entityType = entityKey.GetEntitySet(m_translator.MetadataWorkspace).ElementType; var keyRowType = entityType.GetKeyRowType(); var keyMetadata = m_translator.GetExtractorMetadata(stateEntry.EntitySet, keyRowType); var keyMemberCount = keyRowType.Properties.Count; var keyValues = new PropagatorResult[keyMemberCount]; for (var ordinal = 0; ordinal < keyRowType.Properties.Count; ordinal++) { EdmMember keyMember = keyRowType.Properties[ordinal]; // retrieve information about this key value var keyMemberInformation = keyMetadata.m_memberMap[ordinal]; var keyIdentifier = m_translator.KeyManager.GetKeyIdentifierForMemberOffset(entityKey, ordinal, keyRowType.Properties.Count); object keyValue = null; if (entityKey.IsTemporary) { // If the EntityKey is temporary, we need to retrieve the appropriate // key value from the entity itself (or in this case, the IEntityStateEntry). var entityEntry = stateEntry.StateManager.GetEntityStateEntry(entityKey); Debug.Assert( entityEntry.State == EntityState.Added, "The corresponding entry for a temp EntityKey should be in the Added State."); keyValue = entityEntry.CurrentValues[keyMember.Name]; } else { // Otherwise, we extract the value from within the EntityKey. keyValue = entityKey.FindValueByName(keyMember.Name); } Debug.Assert(keyValue != null, "keyValue should've been retrieved."); // construct propagator result keyValues[ordinal] = PropagatorResult.CreateKeyValue( keyMemberInformation.Flags, keyValue, stateEntry, keyIdentifier); // see UpdateTranslator.Identifiers for information on key identifiers and ordinals } return PropagatorResult.CreateStructuralValue(keyValues, keyMetadata.m_type, false); }