mirror of https://github.com/abpframework/abp.git
3 changed files with 253 additions and 0 deletions
@ -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
|
||||
|
///// <summary>
|
||||
|
///// A shortcut of <see cref="Entity{TPrimaryKey}"/> for most used primary key type (<see cref="int"/>).
|
||||
|
///// </summary>
|
||||
|
//public abstract class Entity : Entity<string>, IEntity<>
|
||||
|
//{
|
||||
|
|
||||
|
//}
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Basic implementation of IEntity interface.
|
||||
|
/// An entity can inherit this class of directly implement to IEntity interface.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPrimaryKey">Type of the primary key of the entity</typeparam>
|
||||
|
public abstract class Entity<TPrimaryKey> : IEntity<TPrimaryKey> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Unique identifier for this entity.
|
||||
|
/// </summary>
|
||||
|
public virtual TPrimaryKey Id { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Checks if this entity is transient (it has not an Id).
|
||||
|
/// </summary>
|
||||
|
/// <returns>True, if this entity is transient</returns>
|
||||
|
public virtual bool IsTransient() |
||||
|
{ |
||||
|
if (EqualityComparer<TPrimaryKey>.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; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
if (obj == null || !(obj is Entity<TPrimaryKey>)) |
||||
|
{ |
||||
|
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<TPrimaryKey>)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<IMayHaveTenant>().TenantId != other.As<IMayHaveTenant>().TenantId)
|
||||
|
//{
|
||||
|
// return false;
|
||||
|
//}
|
||||
|
|
||||
|
//if (this is IMustHaveTenant && other is IMustHaveTenant &&
|
||||
|
// this.As<IMustHaveTenant>().TenantId != other.As<IMustHaveTenant>().TenantId)
|
||||
|
//{
|
||||
|
// return false;
|
||||
|
//}
|
||||
|
|
||||
|
return Id.Equals(other.Id); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
return Id.GetHashCode(); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public static bool operator ==(Entity<TPrimaryKey> left, Entity<TPrimaryKey> right) |
||||
|
{ |
||||
|
if (Equals(left, null)) |
||||
|
{ |
||||
|
return Equals(right, null); |
||||
|
} |
||||
|
|
||||
|
return left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public static bool operator !=(Entity<TPrimaryKey> left, Entity<TPrimaryKey> right) |
||||
|
{ |
||||
|
return !(left == right); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return $"[{GetType().Name} {Id}]"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
namespace Volo.Abp.Domain.Entities |
||||
|
{ |
||||
|
//TODO: Think on Entity class without PK
|
||||
|
///// <summary>
|
||||
|
///// A shortcut of <see cref="IEntity{TPrimaryKey}"/> for most used primary key type (<see cref="int"/>).
|
||||
|
///// </summary>
|
||||
|
//public interface IEntity : IEntity<int>
|
||||
|
//{
|
||||
|
|
||||
|
//}
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines interface for base entity type. All entities in the system must implement this interface.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPrimaryKey">Type of the primary key of the entity</typeparam>
|
||||
|
public interface IEntity<TPrimaryKey> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Unique identifier for this entity.
|
||||
|
/// </summary>
|
||||
|
TPrimaryKey Id { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Checks if this entity is transient (not persisted to database and it has not an <see cref="Id"/>).
|
||||
|
/// </summary>
|
||||
|
/// <returns>True, if this entity is transient</returns>
|
||||
|
bool IsTransient(); |
||||
|
} |
||||
|
} |
||||
@ -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/
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Base class for value objects.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TValueObject">The type of the value object.</typeparam>
|
||||
|
public abstract class ValueObject<TValueObject> : IEquatable<TValueObject> |
||||
|
where TValueObject : ValueObject<TValueObject> |
||||
|
{ |
||||
|
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<TValueObject>; |
||||
|
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<TValueObject> x, ValueObject<TValueObject> 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<TValueObject> x, ValueObject<TValueObject> y) |
||||
|
{ |
||||
|
return !(x == y); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue