using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
namespace Umbraco.Core.Models.EntityBase
{
///
/// Base Abstract Entity
///
[Serializable]
[DataContract(IsReference = true)]
[DebuggerDisplay("Id: {Id}")]
public abstract class Entity : IEntity, IRememberBeingDirty, ICanBeDirty
{
private bool _hasIdentity;
private int? _hash;
private int _id;
private Guid _key;
///
/// Integer Id
///
[DataMember]
public int Id
{
get
{
return _id;
}
set
{
_id = value;
HasIdentity = true;
}
}
///
/// Guid based Id
///
/// The key is currectly used to store the Unique Id from the
/// umbracoNode table, which many of the entities are based on.
[DataMember]
public Guid Key
{
get
{
if (_key == Guid.Empty)
return _id.ToGuid();
return _key;
}
set { _key = value; }
}
///
/// Gets or sets the Created Date
///
[DataMember]
public DateTime CreateDate { get; set; }
///
/// Gets or sets the Modified Date
///
[DataMember]
public DateTime UpdateDate { get; set; }
///
/// Property changed event
///
public event PropertyChangedEventHandler PropertyChanged;
///
/// Method to call on a property setter.
///
/// The property info.
protected virtual void OnPropertyChanged(PropertyInfo propertyInfo)
{
_propertyChangedInfo[propertyInfo.Name] = true;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyInfo.Name));
}
}
internal virtual void ResetIdentity()
{
_hasIdentity = false;
_id = default(int);
}
///
/// Method to call on entity saved when first added
///
internal virtual void AddingEntity()
{
CreateDate = DateTime.Now;
UpdateDate = DateTime.Now;
}
///
/// Method to call on entity saved/updated
///
internal virtual void UpdatingEntity()
{
UpdateDate = DateTime.Now;
}
///
/// Tracks the properties that have changed
///
private readonly IDictionary _propertyChangedInfo = new Dictionary();
///
/// Tracks the properties that we're changed before the last commit (or last call to ResetDirtyProperties)
///
private IDictionary _lastPropertyChangedInfo = null;
///
/// Indicates whether a specific property on the current entity is dirty.
///
/// Name of the property to check
/// True if Property is dirty, otherwise False
public virtual bool IsPropertyDirty(string propertyName)
{
return _propertyChangedInfo.Any(x => x.Key == propertyName);
}
///
/// Indicates whether the current entity is dirty.
///
/// True if entity is dirty, otherwise False
public virtual bool IsDirty()
{
return _propertyChangedInfo.Any();
}
///
/// Indicates that the entity had been changed and the changes were committed
///
///
public bool WasDirty()
{
return _lastPropertyChangedInfo != null && _lastPropertyChangedInfo.Any();
}
///
/// Indicates whether a specific property on the current entity was changed and the changes were committed
///
/// Name of the property to check
/// True if Property was changed, otherwise False. Returns false if the entity had not been previously changed.
public virtual bool WasPropertyDirty(string propertyName)
{
return WasDirty() && _lastPropertyChangedInfo.Any(x => x.Key == propertyName);
}
///
/// Resets the remembered dirty properties from before the last commit
///
public void ForgetPreviouslyDirtyProperties()
{
_lastPropertyChangedInfo.Clear();
}
///
/// Resets dirty properties by clearing the dictionary used to track changes.
///
///
/// Please note that resetting the dirty properties could potentially
/// obstruct the saving of a new or updated entity.
///
public virtual void ResetDirtyProperties()
{
ResetDirtyProperties(true);
}
///
/// Resets dirty properties by clearing the dictionary used to track changes.
///
///
/// true if we are to remember the last changes made after resetting
///
///
/// Please note that resetting the dirty properties could potentially
/// obstruct the saving of a new or updated entity.
///
internal void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
{
if (rememberPreviouslyChangedProperties)
{
//copy the changed properties to the last changed properties
_lastPropertyChangedInfo = _propertyChangedInfo.ToDictionary(v => v.Key, v => v.Value);
}
_propertyChangedInfo.Clear();
}
///
/// Used by inheritors to set the value of properties, this will detect if the property value actually changed and if it did
/// it will ensure that the property has a dirty flag set.
///
///
///
///
/// returns true if the value changed
///
/// This is required because we don't want a property to show up as "dirty" if the value is the same. For example, when we
/// save a document type, nearly all properties are flagged as dirty just because we've 'reset' them, but they are all set
/// to the same value, so it's really not dirty.
///
internal bool SetPropertyValueAndDetectChanges(Func setValue, T value, PropertyInfo propertySelector)
{
var initVal = value;
var newVal = setValue(value);
if (!Equals(initVal, newVal))
{
OnPropertyChanged(propertySelector);
return true;
}
return false;
}
///
/// Indicates whether the current entity has an identity, eg. Id.
///
public virtual bool HasIdentity
{
get
{
return _hasIdentity;
}
protected set
{
_hasIdentity = value;
}
}
public static bool operator ==(Entity left, Entity right)
{
if (ReferenceEquals(null, left))
return false;
return left.Equals(right);
}
public static bool operator !=(Entity left, Entity right)
{
return !(left == right);
}
public virtual bool SameIdentityAs(IEntity other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return SameIdentityAs(other as Entity);
}
public virtual bool Equals(Entity other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return SameIdentityAs(other);
}
public virtual Type GetRealType()
{
return GetType();
}
public virtual bool SameIdentityAs(Entity other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
if (GetType() == other.GetRealType() && HasIdentity && other.HasIdentity)
return other.Id.Equals(Id);
return false;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
return SameIdentityAs(obj as IEntity);
}
public override int GetHashCode()
{
if (!_hash.HasValue)
_hash = !HasIdentity ? new int?(base.GetHashCode()) : new int?(Id.GetHashCode() * 397 ^ GetType().GetHashCode());
return _hash.Value;
}
}
}