e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
413 lines
15 KiB
C#
413 lines
15 KiB
C#
//---------------------------------------------------------------------
|
|
// <copyright file="ObjectStateEntry.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//
|
|
// @owner [....]
|
|
// @backupOwner [....]
|
|
//---------------------------------------------------------------------
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|