2018-01-10 12:48:51 +01:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
|
using System.Runtime.Serialization;
|
|
|
|
|
|
|
2018-01-15 11:32:30 +01:00
|
|
|
|
namespace Umbraco.Core.Models.Entities
|
2018-01-10 12:48:51 +01:00
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Provides a base class for entities.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[Serializable]
|
|
|
|
|
|
[DataContract(IsReference = true)]
|
|
|
|
|
|
[DebuggerDisplay("Id: {" + nameof(Id) + "}")]
|
|
|
|
|
|
public abstract class EntityBase : BeingDirtyBase, IEntity
|
|
|
|
|
|
{
|
2018-10-23 15:04:41 +02:00
|
|
|
|
#if DEBUG_MODEL
|
2018-10-19 08:26:17 +11:00
|
|
|
|
public Guid InstanceId = Guid.NewGuid();
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2018-01-10 12:48:51 +01:00
|
|
|
|
private bool _hasIdentity;
|
|
|
|
|
|
private int _id;
|
|
|
|
|
|
private Guid _key;
|
|
|
|
|
|
private DateTime _createDate;
|
|
|
|
|
|
private DateTime _updateDate;
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
[DataMember]
|
|
|
|
|
|
public int Id
|
|
|
|
|
|
{
|
|
|
|
|
|
get => _id;
|
|
|
|
|
|
set
|
|
|
|
|
|
{
|
2019-02-04 19:52:04 +11:00
|
|
|
|
SetPropertyValueAndDetectChanges(value, ref _id, nameof(Id));
|
2018-01-10 12:48:51 +01:00
|
|
|
|
_hasIdentity = value != 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
[DataMember]
|
|
|
|
|
|
public Guid Key
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
// if an entity does NOT have a key yet, assign one now
|
|
|
|
|
|
if (_key == Guid.Empty)
|
|
|
|
|
|
_key = Guid.NewGuid();
|
|
|
|
|
|
return _key;
|
|
|
|
|
|
}
|
2019-02-04 19:52:04 +11:00
|
|
|
|
set => SetPropertyValueAndDetectChanges(value, ref _key, nameof(Key));
|
2018-01-10 12:48:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
[DataMember]
|
|
|
|
|
|
public DateTime CreateDate
|
|
|
|
|
|
{
|
|
|
|
|
|
get => _createDate;
|
2019-02-04 19:52:04 +11:00
|
|
|
|
set => SetPropertyValueAndDetectChanges(value, ref _createDate, nameof(CreateDate));
|
2018-01-10 12:48:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
[DataMember]
|
|
|
|
|
|
public DateTime UpdateDate
|
|
|
|
|
|
{
|
|
|
|
|
|
get => _updateDate;
|
2019-02-04 19:52:04 +11:00
|
|
|
|
set => SetPropertyValueAndDetectChanges(value, ref _updateDate, nameof(UpdateDate));
|
2018-01-10 12:48:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
[DataMember]
|
|
|
|
|
|
public DateTime? DeleteDate { get; set; } // no change tracking - not persisted
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
[DataMember]
|
|
|
|
|
|
public virtual bool HasIdentity => _hasIdentity;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Resets the entity identity.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
internal virtual void ResetIdentity()
|
|
|
|
|
|
{
|
|
|
|
|
|
_id = default;
|
|
|
|
|
|
_key = Guid.Empty;
|
|
|
|
|
|
_hasIdentity = false;
|
|
|
|
|
|
}
|
2019-06-28 09:19:11 +02:00
|
|
|
|
|
2018-01-10 12:48:51 +01:00
|
|
|
|
public virtual bool Equals(EntityBase other)
|
|
|
|
|
|
{
|
|
|
|
|
|
return other != null && (ReferenceEquals(this, other) || SameIdentityAs(other));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override bool Equals(object obj)
|
|
|
|
|
|
{
|
|
|
|
|
|
return obj != null && (ReferenceEquals(this, obj) || SameIdentityAs(obj as EntityBase));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private bool SameIdentityAs(EntityBase other)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (other == null) return false;
|
|
|
|
|
|
|
|
|
|
|
|
// same identity if
|
|
|
|
|
|
// - same object (reference equals)
|
2019-01-22 18:03:39 -05:00
|
|
|
|
// - or same CLR type, both have identities, and they are identical
|
2018-01-10 12:48:51 +01:00
|
|
|
|
|
|
|
|
|
|
if (ReferenceEquals(this, other))
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
return GetType() == other.GetType() && HasIdentity && other.HasIdentity && Id == other.Id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override int GetHashCode()
|
|
|
|
|
|
{
|
|
|
|
|
|
unchecked
|
|
|
|
|
|
{
|
|
|
|
|
|
var hashCode = HasIdentity.GetHashCode();
|
|
|
|
|
|
hashCode = (hashCode * 397) ^ Id;
|
|
|
|
|
|
hashCode = (hashCode * 397) ^ GetType().GetHashCode();
|
|
|
|
|
|
return hashCode;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-25 15:47:25 +11:00
|
|
|
|
public object DeepClone()
|
2018-01-10 12:48:51 +01:00
|
|
|
|
{
|
|
|
|
|
|
// memberwise-clone (ie shallow clone) the entity
|
|
|
|
|
|
var unused = Key; // ensure that 'this' has a key, before cloning
|
|
|
|
|
|
var clone = (EntityBase) MemberwiseClone();
|
|
|
|
|
|
|
2018-10-23 15:04:41 +02:00
|
|
|
|
#if DEBUG_MODEL
|
2018-10-19 08:26:17 +11:00
|
|
|
|
clone.InstanceId = Guid.NewGuid();
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2018-10-25 15:47:25 +11:00
|
|
|
|
//disable change tracking while we deep clone IDeepCloneable properties
|
2018-01-10 12:48:51 +01:00
|
|
|
|
clone.DisableChangeTracking();
|
|
|
|
|
|
|
|
|
|
|
|
// deep clone ref properties that are IDeepCloneable
|
|
|
|
|
|
DeepCloneHelper.DeepCloneRefProperties(this, clone);
|
|
|
|
|
|
|
2018-10-25 15:47:25 +11:00
|
|
|
|
PerformDeepClone(clone);
|
|
|
|
|
|
|
|
|
|
|
|
// clear changes (ensures the clone has its own dictionaries)
|
2018-01-10 12:48:51 +01:00
|
|
|
|
clone.ResetDirtyProperties(false);
|
2018-10-25 15:47:25 +11:00
|
|
|
|
|
|
|
|
|
|
//re-enable change tracking
|
2018-01-10 12:48:51 +01:00
|
|
|
|
clone.EnableChangeTracking();
|
|
|
|
|
|
|
|
|
|
|
|
return clone;
|
|
|
|
|
|
}
|
2018-10-25 15:47:25 +11:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Used by inheritors to modify the DeepCloning logic
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="clone"></param>
|
|
|
|
|
|
protected virtual void PerformDeepClone(object clone)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
2018-01-10 12:48:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|