diff --git a/src/Umbraco.Core/IntExtensions.cs b/src/Umbraco.Core/IntExtensions.cs index ffd2cc3a1b..ae2c01ef95 100644 --- a/src/Umbraco.Core/IntExtensions.cs +++ b/src/Umbraco.Core/IntExtensions.cs @@ -16,5 +16,17 @@ namespace Umbraco.Core action(i); } } + + /// + /// Creates a Guid based on an integer value + /// + /// value to convert + /// + public static Guid ToGuid(this int value) + { + byte[] bytes = new byte[16]; + BitConverter.GetBytes(value).CopyTo(bytes, 0); + return new Guid(bytes); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/EntityBase/Entity.cs b/src/Umbraco.Core/Models/EntityBase/Entity.cs new file mode 100644 index 0000000000..d18c30c124 --- /dev/null +++ b/src/Umbraco.Core/Models/EntityBase/Entity.cs @@ -0,0 +1,221 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization; + +namespace Umbraco.Core.Models.EntityBase +{ + /// + /// Base Abstract Entity + /// + [Serializable] + [DataContract(IsReference = true)] + public abstract class Entity : IEntity + { + 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 CreatedDate { get; set; } + + /// + /// Gets or sets the Modified Date + /// + [DataMember] + public DateTime ModifiedDate { 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)); + } + } + + /// + /// Method to call on entity saved when first added + /// + internal virtual void AddingEntity() + { + CreatedDate = DateTime.UtcNow; + ModifiedDate = DateTime.UtcNow; + } + + /// + /// Method to call on entity saved/updated + /// + internal virtual void UpdatingEntity() + { + ModifiedDate = DateTime.UtcNow; + } + + /// + /// Tracks the properties that have changed + /// + private readonly IDictionary _propertyChangedInfo = new Dictionary(); + + /// + /// Returns true if the property referenced by the name has been changed on the class + /// + /// + /// + public bool IsPropertyDirty(string propertyName) + { + return _propertyChangedInfo.Any(x => x.Key == propertyName); + } + + /// + /// Returns true if any properties have been changed on the class + /// + /// + public bool IsDirty() + { + return _propertyChangedInfo.Any(); + } + + /// + /// Resets dirty properties by clearing the dictionary used to track changes. + /// + internal void ResetDirtyProperties() + { + _propertyChangedInfo.Clear(); + } + + /// + /// 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; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/EntityBase/IAggreateRoot.cs b/src/Umbraco.Core/Models/EntityBase/IAggreateRoot.cs new file mode 100644 index 0000000000..4bbf151803 --- /dev/null +++ b/src/Umbraco.Core/Models/EntityBase/IAggreateRoot.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Core.Models.EntityBase +{ + /// + /// Marker interface for aggregate roots + /// + public interface IAggreateRoot : IEntity + { + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/EntityBase/ICanBeDirty.cs b/src/Umbraco.Core/Models/EntityBase/ICanBeDirty.cs new file mode 100644 index 0000000000..34dcc2d830 --- /dev/null +++ b/src/Umbraco.Core/Models/EntityBase/ICanBeDirty.cs @@ -0,0 +1,11 @@ +namespace Umbraco.Core.Models.EntityBase +{ + /// + /// An interface that defines the object is tracking property changes and if it is Dirty + /// + public interface ICanBeDirty + { + bool IsDirty(); + bool IsPropertyDirty(string propName); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/EntityBase/IEntity.cs b/src/Umbraco.Core/Models/EntityBase/IEntity.cs new file mode 100644 index 0000000000..2f0d81ee3f --- /dev/null +++ b/src/Umbraco.Core/Models/EntityBase/IEntity.cs @@ -0,0 +1,46 @@ +using System; +using System.Runtime.Serialization; + +namespace Umbraco.Core.Models.EntityBase +{ + /// + /// Defines an Entity. + /// Entities should always have an Id, Created and Modified date + /// + /// The current database schema doesn't provide a modified date + /// for all entities, so this will have to be changed at a later stage. + public interface IEntity + { + /// + /// The Id of the entity + /// + [DataMember] + int Id { get; set; } + + /// + /// 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] + Guid Key { get; set; } + + /// + /// Gets or sets the Created Date + /// + [DataMember] + DateTime CreatedDate { get; set; } + + /// + /// Gets or sets the Modified Date + /// + [DataMember] + DateTime ModifiedDate { get; set; } + + /// + /// Indicates whether the current entity has an identity, eg. Id. + /// + [IgnoreDataMember] + bool HasIdentity { get; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/EntityBase/IValueObject.cs b/src/Umbraco.Core/Models/EntityBase/IValueObject.cs new file mode 100644 index 0000000000..3fb76e598e --- /dev/null +++ b/src/Umbraco.Core/Models/EntityBase/IValueObject.cs @@ -0,0 +1,11 @@ +namespace Umbraco.Core.Models.EntityBase +{ + /// + /// Marker interface for value object, eg. objects without + /// the same kind of identity as an Entity (with its Id). + /// + public interface IValueObject + { + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index c19c511dd0..e37bb2f77f 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -37,6 +37,7 @@ + @@ -91,6 +92,11 @@ + + + + +