Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataEntity / System / Data / Objects / DataClasses / entityreference_tresulttype.cs / 1305376 / entityreference_tresulttype.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.Objects.Internal; using System.Data.Metadata.Edm; using System.Diagnostics; using System.Runtime.Serialization; using System.Linq; namespace System.Data.Objects.DataClasses { ////// Models a relationship end with multiplicity 1. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] [DataContract] [Serializable] public sealed class EntityReference: EntityReference where TEntity : class { // ------ // 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. // Note that this field should no longer be used directly. Instead, use the _wrappedCachedValue // field. This field is retained only for compatability with the serialization format introduced in v1. private TEntity _cachedValue; [NonSerialized] private IEntityWrapper _wrappedCachedValue; // ------------ // 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. /// public EntityReference() { _wrappedCachedValue = EntityWrapperFactory.NullWrapper; } internal EntityReference(IEntityWrapper wrappedOwner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer) : base(wrappedOwner, navigation, relationshipFixer) { _wrappedCachedValue = EntityWrapperFactory.NullWrapper; } // ---------- // Properties // ---------- ////// Stub only please replace with actual implementation /// [System.Xml.Serialization.SoapIgnore] [System.Xml.Serialization.XmlIgnore] public TEntity Value { get { DeferredLoad(); return (TEntity)ReferenceValue.Entity; } set { ReferenceValue = EntityWrapperFactory.WrapEntityUsingContext(value, ObjectContext); } } internal override IEntityWrapper CachedValue { get { return _wrappedCachedValue; } } internal override IEntityWrapper ReferenceValue { get { CheckOwnerNull(); return _wrappedCachedValue; } set { CheckOwnerNull(); //setting to same value is a no-op (SQLBU DT # 446320) //setting to null is a special case because then we will also clear out any Added/Unchanged relationships with key entries, so we can't no-op if Value is null if (value.Entity != null && value.Entity == _wrappedCachedValue.Entity) { return; } if (null != value.Entity) { // Note that this is only done for the case where we are not setting the ref to null because // clearing a ref is okay--it will cause the dependent to become deleted/detached. ValidateOwnerWithRIConstraints(value, value == EntityWrapperFactory.NullWrapper ? null : value.EntityKey, checkBothEnds: true); ObjectContext context = ObjectContext ?? value.Context; if (context != null) { context.ObjectStateManager.TransactionManager.EntityBeingReparented = GetDependentEndOfReferentialConstraint(value.Entity); } try { Add(value, /*applyConstraints*/false); } finally { if (context != null) { context.ObjectStateManager.TransactionManager.EntityBeingReparented = null; } } } else { if (UsingNoTracking) { if (_wrappedCachedValue.Entity != null) { // The other end of relationship can be the EntityReference or EntityCollection // If the other end is EntityReference, its IsLoaded property should be set to FALSE RelatedEnd relatedEnd = GetOtherEndOfRelationship(_wrappedCachedValue); relatedEnd.OnRelatedEndClear(); } _isLoaded = false; } ClearCollectionOrRef(null, null, false); } } } // ------- // Methods // ------- ////// Loads the related entity or entities into the local related end using the supplied MergeOption. /// public override void Load(MergeOption mergeOption) { CheckOwnerNull(); // Validate that the Load is possible bool hasResults; ObjectQuerysourceQuery = ValidateLoad (mergeOption, "EntityReference", out hasResults); _suppressEvents = true; // we do not want any event during the bulk operation try { List refreshedValue = null; if (hasResults) { // Only issue a query if we know it can produce results (in the case of FK, there may not be any // results). refreshedValue = new List (GetResults (sourceQuery)); } if (null == refreshedValue || refreshedValue.Count == 0) { if (!((AssociationType)base.RelationMetadata).IsForeignKey && ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One) { //query returned zero related end; one related end was expected. throw EntityUtil.LessThanExpectedRelatedEntitiesFound(); } else if (mergeOption == MergeOption.OverwriteChanges || mergeOption == MergeOption.PreserveChanges) { // This entity is not related to anything in this AssociationSet and Role on the server. // If there is an existing _cachedValue, we may need to clear it out, based on the MergeOption EntityKey sourceKey = WrappedOwner.EntityKey; EntityUtil.CheckEntityKeyNull(sourceKey); ObjectStateManager.RemoveRelationships(ObjectContext, mergeOption, (AssociationSet)RelationshipSet, sourceKey, (AssociationEndMember)FromEndProperty); } // else this is NoTracking or AppendOnly, and no entity was retrieved by the Load, so there's nothing extra to do // Since we have no value and are not doing a merge, the last step is to set IsLoaded to true _isLoaded = true; } else if (refreshedValue.Count == 1) { Merge (refreshedValue, mergeOption, true /*setIsLoaded*/); } else { // More than 1 result, which is non-recoverable data inconsistency throw EntityUtil.MoreThanExpectedRelatedEntitiesFound(); } } finally { _suppressEvents = false; } // fire the AssociationChange with Refresh OnAssociationChanged(CollectionChangeAction.Refresh, null); } /// /// This operation is not allowed if the owner is null /// ///internal override IEnumerable GetInternalEnumerable() { if (ReferenceValue.Entity != null) { yield return (TEntity)ReferenceValue.Entity; } } internal override IEnumerable GetWrappedEntities() { // return _wrappedCachedValue.Entity == null ? new IEntityWrapper[0] : new IEntityWrapper[] { _wrappedCachedValue }; } /// /// Attaches an entity to the EntityReference. The given /// entity is not assumed to be the complete set of related entities. /// /// Owner and all entities passed in must be in Unchanged or Modified state. /// Deleted elements are allowed only when the state manager is already tracking the relationship /// instance. /// /// The entity to attach to the EntityCollection ///Thrown when ///is null. Thrown when the entity cannot be related via the current relationship end. public void Attach(TEntity entity) { CheckOwnerNull(); EntityUtil.CheckArgumentNull(entity, "entity"); Attach(new IEntityWrapper[] { EntityWrapperFactory.WrapEntityUsingContext(entity, ObjectContext) }, false); } internal override void Include(bool addRelationshipAsUnchanged, bool doAttach) { Debug.Assert(this.ObjectContext != null, "Should not be trying to add entities to state manager if context is null"); // If we have an actual value or a key for this reference, add it to the context if (null != _wrappedCachedValue.Entity) { // Sometimes with mixed POCO and IPOCO, you can get different instances of IEntityWrappers stored in the IPOCO related ends // These should be replaced by the IEntityWrapper that is stored in the context IEntityWrapper identityWrapper = EntityWrapperFactory.WrapEntityUsingContext(_wrappedCachedValue.Entity, WrappedOwner.Context); if (identityWrapper != _wrappedCachedValue) { _wrappedCachedValue = identityWrapper; } IncludeEntity(_wrappedCachedValue, addRelationshipAsUnchanged, doAttach); } else if (DetachedEntityKey != null) { IncludeEntityKey(doAttach); } // else there is nothing to add for this relationship } private void IncludeEntityKey(bool doAttach) { ObjectStateManager manager = this.ObjectContext.ObjectStateManager; bool addNewRelationship = false; bool addKeyEntry = false; EntityEntry existingEntry = manager.FindEntityEntry(DetachedEntityKey); if (existingEntry == null) { // add new key entry and create a relationship with it addKeyEntry = true; addNewRelationship = true; } else { if (existingEntry.IsKeyEntry) { // We have an existing key entry, so just need to add a relationship with it // We know the target end of this relationship is 1..1 or 0..1 since it is a reference, so if the source end is also not Many, we have a 1-to-1 if (FromEndProperty.RelationshipMultiplicity != RelationshipMultiplicity.Many) { // before we add a new relationship to this key entry, make sure it's not already related to something else // We have to explicitly do this here because there are no other checks to make sure a key entry in a 1-to-1 doesn't end up in two of the same relationship foreach (RelationshipEntry relationshipEntry in this.ObjectContext.ObjectStateManager.FindRelationshipsByKey(DetachedEntityKey)) { // only care about relationships in the same AssociationSet and where the key is playing the same role that it plays in this EntityReference if (relationshipEntry.IsSameAssociationSetAndRole((AssociationSet)RelationshipSet, (AssociationEndMember)ToEndMember, DetachedEntityKey) && relationshipEntry.State != EntityState.Deleted) { throw EntityUtil.EntityConflictsWithKeyEntry(); } } } addNewRelationship = true; } else { IEntityWrapper wrappedTarget = existingEntry.WrappedEntity; // Verify that the target entity is in a valid state for adding a relationship if (existingEntry.State == EntityState.Deleted) { throw EntityUtil.UnableToAddRelationshipWithDeletedEntity(); } // We know the target end of this relationship is 1..1 or 0..1 since it is a reference, so if the source end is also not Many, we have a 1-to-1 RelatedEnd relatedEnd = wrappedTarget.RelationshipManager.GetRelatedEndInternal(RelationshipName, RelationshipNavigation.From); if (FromEndProperty.RelationshipMultiplicity != RelationshipMultiplicity.Many && !relatedEnd.IsEmpty()) { // Make sure the target entity is not already related to something else. // devnote: The call to Add below does *not* do this check for the fixup case, so if it's not done here, no failure will occur // and existing relationships may be deleted unexpectedly. RelatedEnd.Include should not remove existing relationships, only add new ones. throw EntityUtil.EntityConflictsWithKeyEntry(); } // We have an existing entity with the same key, just hook up the related ends this.Add(wrappedTarget, applyConstraints: true, addRelationshipAsUnchanged: doAttach, relationshipAlreadyExists: false, allowModifyingOtherEndOfRelationship: true, forceForeignKeyChanges: true); // add to the list of promoted key references so we can cleanup if a failure occurs later manager.TransactionManager.PopulatedEntityReferences.Add(this); } } // For FKs, don't create a key entry and don't create a relationship if (addNewRelationship && !IsForeignKey) { // devnote: If we add any validation here, it needs to go here before adding the key entry, // otherwise we have to clean up that entry if the validation fails if (addKeyEntry) { EntitySet targetEntitySet = DetachedEntityKey.GetEntitySet(this.ObjectContext.MetadataWorkspace); manager.AddKeyEntry(DetachedEntityKey, targetEntitySet); } EntityKey ownerKey = WrappedOwner.EntityKey; EntityUtil.CheckEntityKeyNull(ownerKey); RelationshipWrapper wrapper = new RelationshipWrapper((AssociationSet)RelationshipSet, RelationshipNavigation.From, ownerKey, RelationshipNavigation.To, DetachedEntityKey); manager.AddNewRelation(wrapper, doAttach ? EntityState.Unchanged : EntityState.Added); } } internal override void Exclude() { Debug.Assert(this.ObjectContext != null, "Should not be trying to remove entities from state manager if context is null"); if (null != _wrappedCachedValue.Entity) { // It is possible that _cachedValue was originally null in this graph, but was only set // while the graph was being added, if the DetachedEntityKey matched its key. In that case, // we only want to clear _cachedValue and delete the relationship entry, but not remove the entity // itself from the context. TransactionManager transManager = ObjectContext.ObjectStateManager.TransactionManager; bool doFullRemove = transManager.PopulatedEntityReferences.Contains(this); bool doRelatedEndRemove = transManager.AlignedEntityReferences.Contains(this); // For POCO, if the entity is undergoing snapshot for the first time, then in this step we actually // need to really exclude it rather than just disconnecting it. If we don't, then it has the potential // to remain in the context at the end of the rollback process. if ((transManager.ProcessedEntities == null || !transManager.ProcessedEntities.Contains(_wrappedCachedValue)) && (doFullRemove || doRelatedEndRemove)) { // Retrieve the relationship entry before _cachedValue is set to null during Remove RelationshipEntry relationshipEntry = IsForeignKey ? null : FindRelationshipEntryInObjectStateManager(_wrappedCachedValue); Debug.Assert(IsForeignKey || relationshipEntry != null, "Should have been able to find a valid relationship since _cachedValue is non-null"); // Remove the related ends and mark the relationship as deleted, but don't propagate the changes to the target entity itself Remove(_wrappedCachedValue, doFixup: doFullRemove, deleteEntity: false, deleteOwner: false, applyReferentialConstraints: false, preserveForeignKey: true); // The relationship will now either be detached (if it was previously in the Added state), or Deleted (if it was previously Unchanged) // If it's Deleted, we need to AcceptChanges to get rid of it completely if (relationshipEntry != null && relationshipEntry.State != EntityState.Detached) { relationshipEntry.AcceptChanges(); } // Since this has been processed, remove it from the list if (doFullRemove) { transManager.PopulatedEntityReferences.Remove(this); } else { transManager.AlignedEntityReferences.Remove(this); } } else { ExcludeEntity(_wrappedCachedValue); } } else if (DetachedEntityKey != null) { // there may still be relationship entries with stubs that need to be removed // this works whether we just added the key entry along with the relationship or if it was already existing ExcludeEntityKey(); } // else there is nothing to remove for this relationship } private void ExcludeEntityKey() { EntityKey ownerKey = WrappedOwner.EntityKey; RelationshipEntry relationshipEntry = this.ObjectContext.ObjectStateManager.FindRelationship(RelationshipSet, new KeyValuePair(RelationshipNavigation.From, ownerKey), new KeyValuePair (RelationshipNavigation.To, DetachedEntityKey)); // we may have failed in adding the graph before we actually added this relationship, so make sure we actually found one if (relationshipEntry != null) { relationshipEntry.Delete(/*doFixup*/ false); // If entry was Added before, it is now Detached, otherwise AcceptChanges to detach it if (relationshipEntry.State != EntityState.Detached) { relationshipEntry.AcceptChanges(); } } } internal override void ClearCollectionOrRef(IEntityWrapper wrappedEntity, RelationshipNavigation navigation, bool doCascadeDelete) { if (wrappedEntity == null) { wrappedEntity = EntityWrapperFactory.NullWrapper; } if (null != _wrappedCachedValue.Entity) { // Following condition checks if we have already visited this graph node. If its true then // we should not do fixup because that would cause circular loop if ((wrappedEntity.Entity == _wrappedCachedValue.Entity) && (navigation.Equals(this.RelationshipNavigation))) { Remove(_wrappedCachedValue, /*fixup*/false, /*deleteEntity*/false, /*deleteOwner*/false, /*applyReferentialConstraints*/false, /*preserveForeignKey*/false); } else { Remove(_wrappedCachedValue, /*fixup*/true, doCascadeDelete, /*deleteOwner*/false, /*applyReferentialConstraints*/true, /*preserveForeignKey*/false); } } else { // this entity reference could be replacing a relationship that points to a key entry // we need to search relationships on the Owner entity to see if this is true, and if so remove the relationship entry if (WrappedOwner.Entity != null && WrappedOwner.Context != null && !UsingNoTracking) { EntityEntry ownerEntry = WrappedOwner.Context.ObjectStateManager.GetEntityEntry(WrappedOwner.Entity); ownerEntry.DeleteRelationshipsThatReferenceKeys(this.RelationshipSet, this.ToEndMember); } } // If we have an Owner, clear the DetachedEntityKey. // If we do not have an owner, retain the key so that we can resolve the difference when the entity is attached to a context if (this.WrappedOwner.Entity != null) { // Clear the detachedEntityKey as well. In cases where we have to fix up the detachedEntityKey, we will not always be able to detect // if we have *only* a Deleted relationship for a given entity/relationship/role, so clearing this here will ensure that // even if no other relationships are added, the key value will still be correct. ((EntityReference)this).DetachedEntityKey = null; } } internal override void ClearWrappedValues() { this._cachedValue = null; this._wrappedCachedValue = NullEntityWrapper.NullWrapper; } /// /// /// /// /// ///True if the verify succeeded, False if the Add should no-op internal override bool VerifyEntityForAdd(IEntityWrapper wrappedEntity, bool relationshipAlreadyExists) { if (!relationshipAlreadyExists && this.ContainsEntity(wrappedEntity)) { return false; } this.VerifyType(wrappedEntity); return true; } internal override bool CanSetEntityType(IEntityWrapper wrappedEntity) { return wrappedEntity.Entity is TEntity; } internal override void VerifyType(IEntityWrapper wrappedEntity) { if (!CanSetEntityType(wrappedEntity)) { throw EntityUtil.InvalidContainedTypeReference(wrappedEntity.Entity.GetType().FullName, typeof(TEntity).FullName); } } ////// Disconnected adds are not supported for an EntityReference so we should report this as an error. /// /// The entity to add to the related end in a disconnected state. internal override void DisconnectedAdd(IEntityWrapper wrappedEntity) { CheckOwnerNull(); } ////// Disconnected removes are not supported for an EntityReference so we should report this as an error. /// /// The entity to remove from the related end in a disconnected state. internal override bool DisconnectedRemove(IEntityWrapper wrappedEntity) { CheckOwnerNull(); return false; } ////// Remove from the RelatedEnd /// /// /// ///internal override bool RemoveFromLocalCache(IEntityWrapper wrappedEntity, bool resetIsLoaded, bool preserveForeignKey) { Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null."); Debug.Assert(null == _wrappedCachedValue.Entity || wrappedEntity.Entity == _wrappedCachedValue.Entity, "The specified object is not a part of this relationship."); _wrappedCachedValue = EntityWrapperFactory.NullWrapper; _cachedValue = null; if (resetIsLoaded) { _isLoaded = false; } // This code sets nullable FK properties on a dependent end to null when a relationship has been nulled. if (ObjectContext != null && IsForeignKey && !preserveForeignKey) { NullAllForeignKeys(); } return true; } /// /// Remove from the POCO collection /// /// ///internal override bool RemoveFromObjectCache(IEntityWrapper wrappedEntity) { Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null."); // For POCO entities - clear the CLR reference if (this.TargetAccessor.HasProperty) { this.WrappedOwner.RemoveNavigationPropertyValue(this, (TEntity)wrappedEntity.Entity); } return true; } // Method used to retrieve properties from principal entities. // NOTE: 'properties' list is modified in this method and may already contains some properties. internal override void RetrieveReferentialConstraintProperties(Dictionary > properties, HashSet
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- UITypeEditor.cs
- Constants.cs
- AdditionalEntityFunctions.cs
- WorkerRequest.cs
- ConfigurationProperty.cs
- X509Certificate2Collection.cs
- ContextInformation.cs
- KeyConstraint.cs
- ObjectResult.cs
- PrintPreviewDialog.cs
- DataGridViewCellToolTipTextNeededEventArgs.cs
- DetailsViewModeEventArgs.cs
- RegexGroup.cs
- WinFormsUtils.cs
- DataGridViewImageColumn.cs
- SqlNodeTypeOperators.cs
- Constraint.cs
- IndentTextWriter.cs
- WasHttpModulesInstallComponent.cs
- ExceptionUtility.cs
- WebPart.cs
- GeometryDrawing.cs
- ExternalException.cs
- SessionIDManager.cs
- RectKeyFrameCollection.cs
- CollectionViewGroupRoot.cs
- EntityDataSourceDataSelection.cs
- BufferedStream.cs
- BaseValidatorDesigner.cs
- GacUtil.cs
- SQLByteStorage.cs
- translator.cs
- DeviceSpecific.cs
- OneOfTypeConst.cs
- XMLSyntaxException.cs
- NGCSerializationManagerAsync.cs
- NetStream.cs
- MessageLoggingFilterTraceRecord.cs
- XPathQilFactory.cs
- ImplicitInputBrush.cs
- DataGridViewCellStyle.cs
- HitTestFilterBehavior.cs
- TextTreeInsertUndoUnit.cs
- Point.cs
- CopyOnWriteList.cs
- PlainXmlSerializer.cs
- CookieParameter.cs
- smtppermission.cs
- DataGridTableCollection.cs
- SessionStateContainer.cs
- MessageSecurityTokenVersion.cs
- SqlXml.cs
- ModuleBuilder.cs
- XmlDictionaryString.cs
- AccessDataSource.cs
- SmtpAuthenticationManager.cs
- DataGridViewRowHeightInfoNeededEventArgs.cs
- URL.cs
- PositiveTimeSpanValidatorAttribute.cs
- OLEDB_Util.cs
- AddressingVersion.cs
- XmlCompatibilityReader.cs
- FlowLayoutSettings.cs
- Rectangle.cs
- CustomErrorsSectionWrapper.cs
- WebPartEditVerb.cs
- SafeRightsManagementPubHandle.cs
- DbConnectionFactory.cs
- XamlTreeBuilderBamlRecordWriter.cs
- VectorKeyFrameCollection.cs
- OrderByQueryOptionExpression.cs
- ClientProxyGenerator.cs
- RegexReplacement.cs
- MdiWindowListStrip.cs
- SaveFileDialog.cs
- IdentityManager.cs
- CodeDOMUtility.cs
- BuildTopDownAttribute.cs
- ExtendedPropertyCollection.cs
- Composition.cs
- DataGridViewDataErrorEventArgs.cs
- XPathException.cs
- LongValidator.cs
- Canvas.cs
- ConfigurationSettings.cs
- ObjectDataSourceView.cs
- URL.cs
- MetadataUtil.cs
- CompilerScope.cs
- RemoteHelper.cs
- UIElement3DAutomationPeer.cs
- PropertyTabAttribute.cs
- AspNetSynchronizationContext.cs
- CodeNamespaceCollection.cs
- SecureEnvironment.cs
- HwndSourceParameters.cs
- MetadataPropertyvalue.cs
- DataGridTextColumn.cs
- AsyncCompletedEventArgs.cs
- ZoneLinkButton.cs