File: System\Data\Objects\ObjectStateEntry.cs
Project: ndp\fx\src\DataEntity\System.Data.Entity.csproj (System.Data.Entity)
//---------------------------------------------------------------------
// <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;
            }
        }
    }
}