//--------------------------------------------------------------------- // <copyright file="ObjectStateEntry.cs" company="Microsoft"> // Copyright (c) Microsoft Corporation. All rights reserved. // </copyright> // // @owner Microsoft // @backupOwner Microsoft //--------------------------------------------------------------------- using System.Collections.Generic; using System.Data.Common; using System.Data.Metadata.Edm; using System.Data.Objects.DataClasses; using System.Diagnostics; using System.Collections; namespace System.Data.Objects { // Detached - nothing // Added - _entity & _currentValues only for shadowState // Unchanged - _entity & _currentValues only for shadowState // Unchanged -> Deleted - _entity & _currentValues only for shadowState // Modified - _currentValues & _modifiedFields + _originalValues only on change // Modified -> Deleted - _currentValues & _modifiedFields + _originalValues only on change /// <summary> /// Represets either a entity, entity stub or relationship /// </summary> public abstract class ObjectStateEntry : IEntityStateEntry, IEntityChangeTracker { #region common entry fields internal ObjectStateManager _cache; internal EntitySetBase _entitySet; internal EntityState _state; #endregion #region Constructor // ObjectStateEntry will not be detached and creation will be handled from ObjectStateManager internal ObjectStateEntry(ObjectStateManager cache, EntitySet entitySet, EntityState state) { Debug.Assert(cache != null, "cache cannot be null."); _cache = cache; _entitySet = entitySet; _state = state; } #endregion // Constructor #region Public members /// <summary> /// ObjectStateManager property of ObjectStateEntry. /// </summary> /// <param></param> /// <returns> ObjectStateManager </returns> public ObjectStateManager ObjectStateManager { get { ValidateState(); return _cache; } } /// <summary> Extent property of ObjectStateEntry. </summary> /// <param></param> /// <returns> Extent </returns> public EntitySetBase EntitySet { get { ValidateState(); return _entitySet; } } /// <summary> /// State property of ObjectStateEntry. /// </summary> /// <param></param> /// <returns> DataRowState </returns> public EntityState State { get { return _state; } internal set { _state = value; } } /// <summary> /// Entity property of ObjectStateEntry. /// </summary> /// <param></param> /// <returns> The entity encapsulated by this entry. </returns> abstract public object Entity { get; } /// <summary> /// The EntityKey associated with the ObjectStateEntry /// </summary> abstract public EntityKey EntityKey { get; internal set; } /// <summary> /// Determines if this ObjectStateEntry represents a relationship /// </summary> abstract public bool IsRelationship { get; } /// <summary> /// Gets bit array indicating which properties are modified. /// </summary> abstract internal BitArray ModifiedProperties { get; } BitArray IEntityStateEntry.ModifiedProperties { get { return this.ModifiedProperties; } } /// <summary> /// Original values of entity /// </summary> /// <param></param> /// <returns> DbDataRecord </returns> [DebuggerBrowsable(DebuggerBrowsableState.Never)] // don't have debugger view expand this abstract public DbDataRecord OriginalValues { get; } abstract public OriginalValueRecord GetUpdatableOriginalValues(); /// <summary> /// Current values of entity/ DataRow /// </summary> /// <param></param> /// <returns> DbUpdatableDataRecord </returns> [DebuggerBrowsable(DebuggerBrowsableState.Never)] // don't have debugger view expand this abstract public CurrentValueRecord CurrentValues { get; } /// <summary> /// API to accept the current values as original values and mark the entity as Unchanged. /// </summary> /// <param></param> /// <returns></returns> abstract public void AcceptChanges(); /// <summary> /// API to mark the entity deleted. if entity is in added state, it will be detached /// </summary> /// <param></param> /// <returns> </returns> abstract public void Delete(); /// <summary> /// API to return properties that are marked modified /// </summary> /// <param> </param> /// <returns> IEnumerable of modified properties names, names are in term of c-space </returns> abstract public IEnumerable<string> GetModifiedProperties(); /// <summary> /// set the state to Modified. /// </summary> /// <param></param> /// <returns></returns> /// <exception cref="InvalidOperationException">If State is not Modified or Unchanged</exception> /// abstract public void SetModified(); /// <summary> /// Marks specified property as modified. /// </summary> /// <param name="propertyName">This API recognizes the names in terms of OSpace</param> /// <exception cref="InvalidOperationException">If State is not Modified or Unchanged</exception> /// abstract public void SetModifiedProperty(string propertyName); /// <summary> /// Rejects any changes made to the property with the given name since the property was last loaded, /// attached, saved, or changes were accepted. The orginal value of the property is stored and the /// property will no longer be marked as modified. /// </summary> /// <remarks> /// If the result is that no properties of the entity are marked as modified, then the entity will /// be marked as Unchanged. /// Changes to properties can only rejected for entities that are in the Modified or Unchanged state. /// Calling this method for entities in other states (Added, Deleted, or Detached) will result in /// an exception being thrown. /// Rejecting changes to properties of an Unchanged entity or unchanged properties of a Modifed /// is a no-op. /// </remarks> /// <param name="propertyName">The name of the property to change.</param> abstract public void RejectPropertyChanges(string propertyName); /// <summary> /// Uses DetectChanges to determine whether or not the current value of the property with the given /// name is different from its original value. Note that this may be different from the property being /// marked as modified since a property which has not changed can still be marked as modified. /// </summary> /// <remarks> /// For complex properties, a new instance of the complex object which has all the same property /// values as the original instance is not considered to be different by this method. /// </remarks> /// <param name="propertyName">The name of the property.</param> /// <returns>True if the property has changed; false otherwise.</returns> abstract public bool IsPropertyChanged(string propertyName); /// <summary> /// Returns the RelationshipManager for the entity represented by this ObjectStateEntry. /// Note that a RelationshipManager objects can only be returned if this entry represents a /// full entity. Key-only entries (stubs) and entries representing relationships do not /// have associated RelationshipManagers. /// </summary> /// <exception cref="InvalidOperationException">The entry is a stub or represents a relationship</exception> abstract public RelationshipManager RelationshipManager { get; } /// <summary> /// Changes state of the entry to the specified <paramref name="state"/> /// </summary> /// <param name="state">The requested state</param> abstract public void ChangeState(EntityState state); /// <summary> /// Apply modified properties to the original object. /// </summary> /// <param name="current">object with modified properties</param> abstract public void ApplyCurrentValues(object currentEntity); /// <summary> /// Apply original values to the entity. /// </summary> /// <param name="original">The object with original values</param> abstract public void ApplyOriginalValues(object originalEntity); #endregion // Public members #region IEntityStateEntry IEntityStateManager IEntityStateEntry.StateManager { get { return (IEntityStateManager)this.ObjectStateManager; } } // must explicitly implement this because interface is internal & so is the property on the // class itself -- apparently the compiler won't let anything marked as internal be part of // an interface (even if the interface is also internal) bool IEntityStateEntry.IsKeyEntry { get { return this.IsKeyEntry; } } #endregion // IEntityStateEntry #region Public IEntityChangeTracker /// <summary> /// Used to report that a scalar entity property is about to change /// The current value of the specified property is cached when this method is called. /// </summary> /// <param name="entityMemberName">The name of the entity property that is changing</param> void IEntityChangeTracker.EntityMemberChanging(string entityMemberName) { this.EntityMemberChanging(entityMemberName); } /// <summary> /// Used to report that a scalar entity property has been changed /// The property value that was cached during EntityMemberChanging is now /// added to OriginalValues /// </summary> /// <param name="entityMemberName">The name of the entity property that has changing</param> void IEntityChangeTracker.EntityMemberChanged(string entityMemberName) { this.EntityMemberChanged(entityMemberName); } /// <summary> /// Used to report that a complex property is about to change /// The current value of the specified property is cached when this method is called. /// </summary> /// <param name="entityMemberName">The name of the top-level entity property that is changing</param> /// <param name="complexObject">The complex object that contains the property that is changing</param> /// <param name="complexObjectMemberName">The name of the property that is changing on complexObject</param> void IEntityChangeTracker.EntityComplexMemberChanging(string entityMemberName, object complexObject, string complexObjectMemberName) { this.EntityComplexMemberChanging(entityMemberName, complexObject, complexObjectMemberName); } /// <summary> /// Used to report that a complex property has been changed /// The property value that was cached during EntityMemberChanging is now added to OriginalValues /// </summary> /// <param name="entityMemberName">The name of the top-level entity property that has changed</param> /// <param name="complexObject">The complex object that contains the property that changed</param> /// <param name="complexObjectMemberName">The name of the property that changed on complexObject</param> void IEntityChangeTracker.EntityComplexMemberChanged(string entityMemberName, object complexObject, string complexObjectMemberName) { this.EntityComplexMemberChanged(entityMemberName, complexObject, complexObjectMemberName); } /// <summary> /// Returns the EntityState from the ObjectStateEntry /// </summary> EntityState IEntityChangeTracker.EntityState { get { return this.State; } } #endregion // IEntityChangeTracker #region Internal members abstract internal bool IsKeyEntry { get; } abstract internal int GetFieldCount(StateManagerTypeMetadata metadata); abstract internal Type GetFieldType(int ordinal, StateManagerTypeMetadata metadata); abstract internal string GetCLayerName(int ordinal, StateManagerTypeMetadata metadata); abstract internal int GetOrdinalforCLayerName(string name, StateManagerTypeMetadata metadata); abstract internal void RevertDelete(); abstract internal void SetModifiedAll(); abstract internal void EntityMemberChanging(string entityMemberName); abstract internal void EntityMemberChanged(string entityMemberName); abstract internal void EntityComplexMemberChanging(string entityMemberName, object complexObject, string complexObjectMemberName); abstract internal void EntityComplexMemberChanged(string entityMemberName, object complexObject, string complexObjectMemberName); /// <summary> /// Reuse or create a new (Entity)DataRecordInfo. /// </summary> abstract internal DataRecordInfo GetDataRecordInfo(StateManagerTypeMetadata metadata, object userObject); virtual internal void Reset() { _cache = null; _entitySet = null; _state = EntityState.Detached; } internal void ValidateState() { if (_state == EntityState.Detached) { throw EntityUtil.ObjectStateEntryinInvalidState(); } Debug.Assert(null != _cache, "null ObjectStateManager"); Debug.Assert(null != _entitySet, "null EntitySetBase"); } #endregion // Internal members } internal struct StateManagerValue { internal StateManagerMemberMetadata memberMetadata; internal object userObject; internal object originalValue; internal StateManagerValue(StateManagerMemberMetadata metadata, object instance, object value) { memberMetadata = metadata; userObject = instance; originalValue = value; } } internal enum ObjectStateValueRecord { OriginalReadonly = 0, CurrentUpdatable = 1, OriginalUpdatableInternal = 2, OriginalUpdatablePublic = 3, } // This class is used in Referential Integrity Constraints feature. // It is used to get around the problem of enumerating dictionary contents, // but allowing update of the value without breaking the enumerator. internal sealed class IntBox { private int val; internal IntBox(int val) { this.val = val; } internal int Value { get { return val; } set { val = value; } } } }