ABP.IO 最佳實踐 Entity
搬運一下官方文件
實體最佳實踐 & 約定
實體
每個聚合根也是一個實體, 所以這些規則對聚合根也是有效的, 除非聚合根的某些規則覆蓋了它們.
主構造函數
推薦 定義一個 主構造函數 確保實體在創建時的有效性, 在代碼中通過主構造函數創建實體的新實例.
推薦 根據需求把主構造函數定義為public, internal或 protected internal. 如果它不是 public 的, 那麼應該由領域服務來創建實體.
推薦 總是在主構造函數中初始化子集合.
不推薦 在主構造函數中生成 Guid 鍵, 應該將其做為參數獲取, 在調用時推薦使用 IGuidGenerator生成新的Guid 值做為參數.
聚合根
主鍵
推薦 總是使用Id屬性做為聚合根主鍵.
不推薦 在聚合根中使用 複合主鍵.
推薦 所有的聚合根都使用 Guid 類型 主鍵.
示例
聚合根
public class Issue : FullAuditedAggregateRoot<Guid> //使用Guid作为键/标识符
{
public virtual string Title { get; private set; } //使用 SetTitle() 方法set
public virtual string Text { get; set; } //可以直接set,null值也是允许的
public virtual Guid? MilestoneId { get; set; } //引用其他聚合根
public virtual bool IsClosed { get; private set; }
public virtual IssueCloseReason? CloseReason { get; private set; } //一个枚举类型
public virtual Collection<IssueLabel> Labels { get; protected set; } //子集合
protected Issue()
{
/* 此构造函数是提供给ORM用来从数据库中获取实体.
* - 无需初始化Labels集合
因为它会被来自数据库的值覆盖.
- It's protected since proxying and deserialization tools
可能不适用于私有构造函数.
*/
}
//主构造函数
public Issue(
Guid id, //从调用代码中获取Guid值
[NotNull] string title, //表示标题不能为空.
string text = null,
Guid? milestoneId = null) //可选参数
{
Id = id;
Title = Check.NotNullOrWhiteSpace(title, nameof(title)); //验证
Text = text;
MilestoneId = milestoneId;
Labels = new Collection<IssueLabel>(); //总是初始化子集合
}
public virtual void SetTitle([NotNull] string title)
{
Title = Check.NotNullOrWhiteSpace(title, nameof(title)); //验证
}
/* AddLabel和RemoveLabel方法管理Labels集合
* 安全的方式(防止两次添加相同的标签) */
public virtual void AddLabel(Guid labelId)
{
if (Labels.Any(l => l.LabelId == labelId))
{
return;
}
Labels.Add(new IssueLabel(Id, labelId));
}
public virtual void RemoveLabel(Guid labelId)
{
Labels.RemoveAll(l => l.LabelId == labelId);
}
/* Close和ReOpen方法可保护一致性
* IsClosed 与 CloseReason 属性. */
public virtual void Close(IssueCloseReason reason)
{
IsClosed = true;
CloseReason = reason;
}
public virtual void ReOpen()
{
IsClosed = false;
CloseReason = null;
}
}
實體
public class IssueLabel : Entity
{
public virtual Guid IssueId { get; private set; }
public virtual Guid LabelId { get; private set; }
protected IssueLabel()
{
}
public IssueLabel(Guid issueId, Guid labelId)
{
IssueId = issueId;
LabelId = labelId;
}
}
參考文獻

PS5
本文章從點部落遷移至 Writerside
13 October 2025