최근에 “강력한 형식의 Id” 라는 주제가 눈에 많이 띄더군요.
하여, Entity 클래스를 도메인 클래스로 혼용할 수 있도록 EntityBase를 정의해봤습니다.
어떤가 한 번 봐주세요.
정의
public readonly record struct Id<T>(Guid Value)
{
public static Id<T> NewId => new(Guid.NewGuid());
public static readonly Id<T> Empty = new(Guid.Empty);
}
public abstract class EntityBase<T> where T : class
{
protected Id<T> Id { get; } = Id<T>.NewId;
public override bool Equals(object? obj) =>
obj is EntityBase<T> entity && Id.Equals(entity.Id);
public override int GetHashCode() =>
Id.GetHashCode();
}
특징
- 프로세스 간 안전한 정체성 비교
- 인스턴스를 Dictionary의 Key로 안전하게 사용
- 간결한 해시 계산
- Id와 관련한 실수의 여지 없음.
- 외부 도메인 로직에서 Id 가 안 보임.
예제
class A : EntityBase<A> { }
class B : EntityBase<B> { }
var a = new A();
var b = new B();
Console.WriteLine($"var a = new A();");
Console.WriteLine($"var b = new B();");
Console.WriteLine($"a.Equals(b) = {a.Equals(b)}");
var aa = a;
Console.WriteLine($"var aa = a;");
Console.WriteLine($"a.Equals(aa) = {a.Equals(aa)}");
aa = new A();
Console.WriteLine($"aa = new A();");
Console.WriteLine($"a.Equals(aa) = {a.Equals(aa)}");
a = _dbContext.Set<A>.First();
aa = _dbContext.Set<A>.First();
Console.WriteLine($"a = _dbContext.Set<A>.First();");
Console.WriteLine($"aa = _dbContext.Set<A>.First();");
Console.WriteLine($"a.Equals(aa) = {a.Equals(aa)}");
결과
var a = new A();
var b = new B();
a.Equals(b) = False
var aa = a;
a.Equals(aa) = True
aa = new A();
a.Equals(aa) = False
a = _dbContext.Set<A>.First();
aa = _dbContext.Set<A>.First();
a.Equals(aa) = True