From d2b2d48888daa88ce5d8f9da6f68b1ef3e3e12e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 9 Dec 2016 14:35:44 +0300 Subject: [PATCH] Added Entity and ValueObject classes. --- .../Volo/Abp/Domain/Entities/Entity.cs | 127 ++++++++++++++++++ .../Volo/Abp/Domain/Entities/IEntity.cs | 29 ++++ .../Volo/Abp/Domain/Values/ValueObject.cs | 97 +++++++++++++ 3 files changed, 253 insertions(+) create mode 100644 src/Volo.Abp/Volo/Abp/Domain/Entities/Entity.cs create mode 100644 src/Volo.Abp/Volo/Abp/Domain/Entities/IEntity.cs create mode 100644 src/Volo.Abp/Volo/Abp/Domain/Values/ValueObject.cs diff --git a/src/Volo.Abp/Volo/Abp/Domain/Entities/Entity.cs b/src/Volo.Abp/Volo/Abp/Domain/Entities/Entity.cs new file mode 100644 index 0000000000..0273ba57b5 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Domain/Entities/Entity.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Volo.Abp.Domain.Entities +{ + //TODO: Think on Entity class without PK + ///// + ///// A shortcut of for most used primary key type (). + ///// + //public abstract class Entity : Entity, IEntity<> + //{ + + //} + + /// + /// Basic implementation of IEntity interface. + /// An entity can inherit this class of directly implement to IEntity interface. + /// + /// Type of the primary key of the entity + public abstract class Entity : IEntity + { + /// + /// Unique identifier for this entity. + /// + public virtual TPrimaryKey Id { get; set; } + + /// + /// Checks if this entity is transient (it has not an Id). + /// + /// True, if this entity is transient + public virtual bool IsTransient() + { + if (EqualityComparer.Default.Equals(Id, default(TPrimaryKey))) + { + return true; + } + + //Workaround for EF Core since it sets int/long to min value when attaching to dbcontext + if (typeof(TPrimaryKey) == typeof(int)) + { + return Convert.ToInt32(Id) <= 0; + } + + if (typeof(TPrimaryKey) == typeof(long)) + { + return Convert.ToInt64(Id) <= 0; + } + + return false; + } + + /// + public override bool Equals(object obj) + { + if (obj == null || !(obj is Entity)) + { + return false; + } + + //Same instances must be considered as equal + if (ReferenceEquals(this, obj)) + { + return true; + } + + //Transient objects are not considered as equal + var other = (Entity)obj; + if (IsTransient() && other.IsTransient()) + { + return false; + } + + //Must have a IS-A relation of types or must be same type + var typeOfThis = GetType().GetTypeInfo(); + var typeOfOther = other.GetType().GetTypeInfo(); + if (!typeOfThis.IsAssignableFrom(typeOfOther) && !typeOfOther.IsAssignableFrom(typeOfThis)) + { + return false; + } + + //TODO: How to handle this? + //if (this is IMayHaveTenant && other is IMayHaveTenant && + // this.As().TenantId != other.As().TenantId) + //{ + // return false; + //} + + //if (this is IMustHaveTenant && other is IMustHaveTenant && + // this.As().TenantId != other.As().TenantId) + //{ + // return false; + //} + + return Id.Equals(other.Id); + } + + /// + public override int GetHashCode() + { + return Id.GetHashCode(); + } + + /// + public static bool operator ==(Entity left, Entity right) + { + if (Equals(left, null)) + { + return Equals(right, null); + } + + return left.Equals(right); + } + + /// + public static bool operator !=(Entity left, Entity right) + { + return !(left == right); + } + + /// + public override string ToString() + { + return $"[{GetType().Name} {Id}]"; + } + } +} diff --git a/src/Volo.Abp/Volo/Abp/Domain/Entities/IEntity.cs b/src/Volo.Abp/Volo/Abp/Domain/Entities/IEntity.cs new file mode 100644 index 0000000000..309c810d2c --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Domain/Entities/IEntity.cs @@ -0,0 +1,29 @@ +namespace Volo.Abp.Domain.Entities +{ + //TODO: Think on Entity class without PK + ///// + ///// A shortcut of for most used primary key type (). + ///// + //public interface IEntity : IEntity + //{ + + //} + + /// + /// Defines interface for base entity type. All entities in the system must implement this interface. + /// + /// Type of the primary key of the entity + public interface IEntity + { + /// + /// Unique identifier for this entity. + /// + TPrimaryKey Id { get; set; } + + /// + /// Checks if this entity is transient (not persisted to database and it has not an ). + /// + /// True, if this entity is transient + bool IsTransient(); + } +} diff --git a/src/Volo.Abp/Volo/Abp/Domain/Values/ValueObject.cs b/src/Volo.Abp/Volo/Abp/Domain/Values/ValueObject.cs new file mode 100644 index 0000000000..f271065f81 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Domain/Values/ValueObject.cs @@ -0,0 +1,97 @@ +using System; +using System.Linq; +using System.Reflection; + +namespace Volo.Abp.Domain.Values +{ + //Inspired from https://blogs.msdn.microsoft.com/cesardelatorre/2011/06/06/implementing-a-value-object-base-class-supertype-patternddd-patterns-related/ + + /// + /// Base class for value objects. + /// + /// The type of the value object. + public abstract class ValueObject : IEquatable + where TValueObject : ValueObject + { + public bool Equals(TValueObject other) + { + if ((object)other == null) + { + return false; + } + + var publicProperties = GetType().GetTypeInfo().GetProperties(); + if (!publicProperties.Any()) + { + return true; + } + + return publicProperties.All(property => Equals(property.GetValue(this, null), property.GetValue(other, null))); + } + + public override bool Equals(object obj) + { + if (obj == null) + { + return false; + } + + var item = obj as ValueObject; + return (object)item != null && Equals((TValueObject)item); + + } + + public override int GetHashCode() + { + const int index = 1; + const int initialHasCode = 31; + + var publicProperties = GetType().GetTypeInfo().GetProperties(); + + if (!publicProperties.Any()) + { + return initialHasCode; + } + + var hashCode = initialHasCode; + var changeMultiplier = false; + + foreach (var property in publicProperties) + { + var value = property.GetValue(this, null); + + if (value == null) + { + //support {"a",null,null,"a"} != {null,"a","a",null} + hashCode = hashCode ^ (index * 13); + continue; + } + + hashCode = hashCode * (changeMultiplier ? 59 : 114) + value.GetHashCode(); + changeMultiplier = !changeMultiplier; + } + + return hashCode; + } + + public static bool operator ==(ValueObject x, ValueObject y) + { + if (ReferenceEquals(x, y)) + { + return true; + } + + if (((object)x == null) || ((object)y == null)) + { + return false; + } + + return x.Equals(y); + } + + public static bool operator !=(ValueObject x, ValueObject y) + { + return !(x == y); + } + } +}