Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataEntity / System / Data / Objects / DataClasses / EntityReference.cs / 4 / EntityReference.cs
//---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.Common; using System.Data.Common.CommandTrees; using System.Data.Metadata.Edm; using System.Diagnostics; using System.Runtime.Serialization; namespace System.Data.Objects.DataClasses { ////// Models a relationship end with multiplicity 1. /// [DataContract] [Serializable] public abstract class EntityReference : RelatedEnd { // ------ // Fields // ------ // The following fields are serialized. Adding or removing a serialized field is considered // a breaking change. This includes changing the field type or field name of existing // serialized fields. If you need to make this kind of change, it may be possible, but it // will require some custom serialization/deserialization code. // The following field is valid only for detached EntityReferences, see EntityKey property for more details. private EntityKey _detachedEntityKey = null; // ------------ // Constructors // ------------ ////// The default constructor is required for some serialization scenarios. It should not be used to /// create new EntityReferences. Use the GetRelatedReference or GetRelatedEnd methods on the RelationshipManager /// class instead. /// protected EntityReference() { } internal EntityReference(IEntityWithRelationships owner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer) : base(owner, navigation, relationshipFixer) { } ////// Returns the EntityKey of the target entity associated with this EntityReference. /// /// Is non-null in the following scenarios: /// (a) Entities are tracked by a context and an Unchanged or Added client-side relationships exists for this EntityReference's owner with the /// same RelationshipName and source role. This relationship could have been created explicitly by the user (e.g. by setting /// the EntityReference.Value, setting this property directly, or by calling EntityCollection.Add) or automatically through span queries. /// (b) If the EntityKey was non-null before detaching an entity from the context, it will still be non-null after detaching, until any operation /// occurs that would set it to null, as described below. /// (c) Entities are detached and the EntityKey is explicitly set to non-null by the user. /// (d) Entity graph was created using a NoTracking query with full span /// /// Is null in the following scenarios: /// (a) Entities are tracked by a context but there is no Unchanged or Added client-side relationship for this EntityReference's owner with the /// same RelationshipName and source role. /// (b) Entities are tracked by a context and a relationship exists, but the target entity has a temporary key (i.e. it is Added) or the key /// is one of the special keys /// (c) Entities are detached and the relationship was explicitly created by the user. /// [DataMember] public EntityKey EntityKey { // This is the only scenario where it is valid to have a null Owner, so don't check it get { if (this.ObjectContext != null && !UsingNoTracking) { Debug.Assert(this.Owner != null, "Unexpected null Owner on EntityReference attached to a context"); EntityKey attachedKey = null; // If this EntityReference contains an entity, look up the key on that object if (CachedValue != null) { // Since this EntityReference is attached to a context, the contained entity must // also be attached and will therefore always have a key attachedKey = this.ObjectContext.ObjectStateManager.GetEntityKey(CachedValue); if (!IsValidEntityKeyType(attachedKey)) { // don't return temporary or special keys from this property attachedKey = null; } } else { // There could still be an Added or Unchanged relationship with a stub entry EntityKey ownerKey = ObjectContext.FindEntityKey(this.Owner, this.ObjectContext); foreach (ObjectStateEntry relationshipEntry in this.ObjectContext.ObjectStateManager.FindRelationshipsByKey(ownerKey)) { // We only care about the relationships that match the AssociationSet and source role for the owner of this EntityReference if (relationshipEntry.State != EntityState.Deleted && relationshipEntry.IsSameAssociationSetAndRole((AssociationSet)RelationshipSet, ownerKey, (AssociationEndMember)this.FromEndProperty)) { Debug.Assert(attachedKey == null, "Found more than one non-Deleted relationship for the same AssociationSet and source role"); attachedKey = relationshipEntry.Wrapper.GetOtherEntityKey(ownerKey); // key should never be temporary or special since it came from a key entry } } } Debug.Assert(attachedKey == null || IsValidEntityKeyType(attachedKey), "Unexpected temporary or special key"); return attachedKey; } else { return _detachedEntityKey; } } set { if (value != null && value == EntityKey) { // "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.Owner != 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) { // delegate to Value property for all validation and firing of events this.ReferenceValue = null; } else { // Verify that the key has the right EntitySet for this RelationshipSet EntitySet targetEntitySet = value.GetEntitySet(ObjectContext.MetadataWorkspace); CheckRelationEntitySet(targetEntitySet); value.ValidateEntityKey(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; ObjectStateEntry targetEntry = manager.FindObjectStateEntry(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.Entity; } 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 = true; addNewRelationship = true; } if (addNewRelationship) { EntityKey ownerKey = ValidateOwnerWithRIConstraints(); // Verify that the owner is in a valid state for adding a relationship ValidateStateForAdd(this.Owner); if (addKeyEntry) { manager.AddKeyEntry(value, targetEntitySet); } // First, clear any existing relationships ClearCollectionOrRef(null, null, /*doCascadeDelete*/ false); // Then add the new one RelationshipWrapper wrapper = new RelationshipWrapper((AssociationSet)RelationshipSet, RelationshipNavigation.From, ownerKey, RelationshipNavigation.To, value); manager.AddNewRelation(wrapper, EntityState.Added); } } } 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 EntityKey AttachedEntityKey { get { Debug.Assert(this.ObjectContext != null && !UsingNoTracking, "Should only need to access AttachedEntityKey property on attached EntityReferences"); return this.EntityKey; } } internal EntityKey DetachedEntityKey { get { return _detachedEntityKey; } set { _detachedEntityKey = value; } } internal abstract object CachedValue { get; } internal abstract object ReferenceValue { get; set; } internal EntityKey ValidateOwnerWithRIConstraints() { EntityKey ownerKey = ObjectStateManager.FindKeyOnEntityWithRelationships(Owner); /* Check if Referential Constraints are violated */ if ((object)ownerKey != null && !ownerKey.IsTemporary && IsDependentEndOfReferentialConstraint()) { throw EntityUtil.CannotChangeReferentialConstraintProperty(); } return ownerKey; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.Common; using System.Data.Common.CommandTrees; using System.Data.Metadata.Edm; using System.Diagnostics; using System.Runtime.Serialization; namespace System.Data.Objects.DataClasses { ////// Models a relationship end with multiplicity 1. /// [DataContract] [Serializable] public abstract class EntityReference : RelatedEnd { // ------ // Fields // ------ // The following fields are serialized. Adding or removing a serialized field is considered // a breaking change. This includes changing the field type or field name of existing // serialized fields. If you need to make this kind of change, it may be possible, but it // will require some custom serialization/deserialization code. // The following field is valid only for detached EntityReferences, see EntityKey property for more details. private EntityKey _detachedEntityKey = null; // ------------ // Constructors // ------------ ////// The default constructor is required for some serialization scenarios. It should not be used to /// create new EntityReferences. Use the GetRelatedReference or GetRelatedEnd methods on the RelationshipManager /// class instead. /// protected EntityReference() { } internal EntityReference(IEntityWithRelationships owner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer) : base(owner, navigation, relationshipFixer) { } ////// Returns the EntityKey of the target entity associated with this EntityReference. /// /// Is non-null in the following scenarios: /// (a) Entities are tracked by a context and an Unchanged or Added client-side relationships exists for this EntityReference's owner with the /// same RelationshipName and source role. This relationship could have been created explicitly by the user (e.g. by setting /// the EntityReference.Value, setting this property directly, or by calling EntityCollection.Add) or automatically through span queries. /// (b) If the EntityKey was non-null before detaching an entity from the context, it will still be non-null after detaching, until any operation /// occurs that would set it to null, as described below. /// (c) Entities are detached and the EntityKey is explicitly set to non-null by the user. /// (d) Entity graph was created using a NoTracking query with full span /// /// Is null in the following scenarios: /// (a) Entities are tracked by a context but there is no Unchanged or Added client-side relationship for this EntityReference's owner with the /// same RelationshipName and source role. /// (b) Entities are tracked by a context and a relationship exists, but the target entity has a temporary key (i.e. it is Added) or the key /// is one of the special keys /// (c) Entities are detached and the relationship was explicitly created by the user. /// [DataMember] public EntityKey EntityKey { // This is the only scenario where it is valid to have a null Owner, so don't check it get { if (this.ObjectContext != null && !UsingNoTracking) { Debug.Assert(this.Owner != null, "Unexpected null Owner on EntityReference attached to a context"); EntityKey attachedKey = null; // If this EntityReference contains an entity, look up the key on that object if (CachedValue != null) { // Since this EntityReference is attached to a context, the contained entity must // also be attached and will therefore always have a key attachedKey = this.ObjectContext.ObjectStateManager.GetEntityKey(CachedValue); if (!IsValidEntityKeyType(attachedKey)) { // don't return temporary or special keys from this property attachedKey = null; } } else { // There could still be an Added or Unchanged relationship with a stub entry EntityKey ownerKey = ObjectContext.FindEntityKey(this.Owner, this.ObjectContext); foreach (ObjectStateEntry relationshipEntry in this.ObjectContext.ObjectStateManager.FindRelationshipsByKey(ownerKey)) { // We only care about the relationships that match the AssociationSet and source role for the owner of this EntityReference if (relationshipEntry.State != EntityState.Deleted && relationshipEntry.IsSameAssociationSetAndRole((AssociationSet)RelationshipSet, ownerKey, (AssociationEndMember)this.FromEndProperty)) { Debug.Assert(attachedKey == null, "Found more than one non-Deleted relationship for the same AssociationSet and source role"); attachedKey = relationshipEntry.Wrapper.GetOtherEntityKey(ownerKey); // key should never be temporary or special since it came from a key entry } } } Debug.Assert(attachedKey == null || IsValidEntityKeyType(attachedKey), "Unexpected temporary or special key"); return attachedKey; } else { return _detachedEntityKey; } } set { if (value != null && value == EntityKey) { // "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.Owner != 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) { // delegate to Value property for all validation and firing of events this.ReferenceValue = null; } else { // Verify that the key has the right EntitySet for this RelationshipSet EntitySet targetEntitySet = value.GetEntitySet(ObjectContext.MetadataWorkspace); CheckRelationEntitySet(targetEntitySet); value.ValidateEntityKey(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; ObjectStateEntry targetEntry = manager.FindObjectStateEntry(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.Entity; } 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 = true; addNewRelationship = true; } if (addNewRelationship) { EntityKey ownerKey = ValidateOwnerWithRIConstraints(); // Verify that the owner is in a valid state for adding a relationship ValidateStateForAdd(this.Owner); if (addKeyEntry) { manager.AddKeyEntry(value, targetEntitySet); } // First, clear any existing relationships ClearCollectionOrRef(null, null, /*doCascadeDelete*/ false); // Then add the new one RelationshipWrapper wrapper = new RelationshipWrapper((AssociationSet)RelationshipSet, RelationshipNavigation.From, ownerKey, RelationshipNavigation.To, value); manager.AddNewRelation(wrapper, EntityState.Added); } } } 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 EntityKey AttachedEntityKey { get { Debug.Assert(this.ObjectContext != null && !UsingNoTracking, "Should only need to access AttachedEntityKey property on attached EntityReferences"); return this.EntityKey; } } internal EntityKey DetachedEntityKey { get { return _detachedEntityKey; } set { _detachedEntityKey = value; } } internal abstract object CachedValue { get; } internal abstract object ReferenceValue { get; set; } internal EntityKey ValidateOwnerWithRIConstraints() { EntityKey ownerKey = ObjectStateManager.FindKeyOnEntityWithRelationships(Owner); /* Check if Referential Constraints are violated */ if ((object)ownerKey != null && !ownerKey.IsTemporary && IsDependentEndOfReferentialConstraint()) { throw EntityUtil.CannotChangeReferentialConstraintProperty(); } return ownerKey; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- PrimitiveType.cs
- DataMisalignedException.cs
- OleDbWrapper.cs
- ProxyHwnd.cs
- JsonByteArrayDataContract.cs
- DesignerFrame.cs
- TypedTableBase.cs
- DocumentOrderQuery.cs
- ByteAnimationBase.cs
- PrintDialogException.cs
- EventManager.cs
- ResourceBinder.cs
- HttpResponseInternalBase.cs
- CompressStream.cs
- GeometryHitTestParameters.cs
- BackgroundFormatInfo.cs
- AsyncCompletedEventArgs.cs
- FormattedText.cs
- WebUtil.cs
- RightsManagementInformation.cs
- Normalization.cs
- AddInDeploymentState.cs
- xmlfixedPageInfo.cs
- XmlSerializationGeneratedCode.cs
- PolygonHotSpot.cs
- HtmlHead.cs
- XmlSchemaAnyAttribute.cs
- SafeHandles.cs
- Rules.cs
- DefaultAssemblyResolver.cs
- Expander.cs
- SqlBooleanMismatchVisitor.cs
- XmlSchemaGroupRef.cs
- FormViewAutoFormat.cs
- ActivityExecutorSurrogate.cs
- SqlUtils.cs
- AssemblyHelper.cs
- BinaryFormatterWriter.cs
- ControllableStoryboardAction.cs
- ModelItem.cs
- XmlTextReader.cs
- XmlSchemaSimpleTypeUnion.cs
- StyleXamlTreeBuilder.cs
- WCFBuildProvider.cs
- HttpHandlerAction.cs
- CannotUnloadAppDomainException.cs
- HandlerFactoryWrapper.cs
- Baml2006ReaderSettings.cs
- ActivationProxy.cs
- DataServiceEntityAttribute.cs
- Int32Rect.cs
- WebPartDescriptionCollection.cs
- DataGridDesigner.cs
- DomainConstraint.cs
- ObjectKeyFrameCollection.cs
- ClonableStack.cs
- SharedStream.cs
- RadioButton.cs
- ColumnWidthChangingEvent.cs
- XmlC14NWriter.cs
- MultipartContentParser.cs
- OutOfProcStateClientManager.cs
- ObjectItemAttributeAssemblyLoader.cs
- FileIOPermission.cs
- DataGridCellAutomationPeer.cs
- XmlUtilWriter.cs
- VisualTransition.cs
- FontFamilyConverter.cs
- KeyboardEventArgs.cs
- DiffuseMaterial.cs
- SafeCoTaskMem.cs
- Paragraph.cs
- XsltContext.cs
- OrderByBuilder.cs
- NetMsmqSecurityMode.cs
- OperationAbortedException.cs
- SafeFileMapViewHandle.cs
- UniqueID.cs
- SafeFileMappingHandle.cs
- SqlUDTStorage.cs
- CorruptStoreException.cs
- DbParameterHelper.cs
- ApplicationId.cs
- MouseOverProperty.cs
- ResourcesBuildProvider.cs
- RemoteWebConfigurationHostStream.cs
- X509ThumbprintKeyIdentifierClause.cs
- SafeProcessHandle.cs
- SendingRequestEventArgs.cs
- IfAction.cs
- TouchDevice.cs
- FormsIdentity.cs
- ClientSponsor.cs
- GeneratedCodeAttribute.cs
- CodeSnippetExpression.cs
- GridViewRowEventArgs.cs
- SQLDateTime.cs
- EntityViewGenerator.cs
- ValueUtilsSmi.cs
- Rotation3D.cs