什么是自有实体?何时以及为什么在 Entity Framework Core 中使用 Owned Entity?
Posted
技术标签:
【中文标题】什么是自有实体?何时以及为什么在 Entity Framework Core 中使用 Owned Entity?【英文标题】:What is Owned Entity? When and why to use Owned Entity in Entity Framework Core? 【发布时间】:2020-08-25 15:19:32 【问题描述】:我正在学习 Entity Framework Core。我在几乎所有教程中都遇到过“自有实体”一词。
这是一个在 Entity Framework Core 中使用自有实体的示例
工作实体:
public class Job : Entity
public HiringManagerName HiringManagerName get; private set;
HiringManagerName 值对象:
public class HiringManagerName : ValueObject
public string First get;
public string Last get;
protected HiringManagerName()
private HiringManagerName(string first, string last)
: this()
First = first;
Last = last;
public static Result<HiringManagerName> Create(string firstName, string lastName)
if (string.IsNullOrWhiteSpace(firstName))
return Result.Failure<HiringManagerName>("First name should not be empty");
if (string.IsNullOrWhiteSpace(lastName))
return Result.Failure<HiringManagerName>("Last name should not be empty");
firstName = firstName.Trim();
lastName = lastName.Trim();
if (firstName.Length > 200)
return Result.Failure<HiringManagerName>("First name is too long");
if (lastName.Length > 200)
return Result.Failure<HiringManagerName>("Last name is too long");
return Result.Success(new HiringManagerName(firstName, lastName));
protected override IEnumerable<object> GetEqualityComponents()
yield return First;
yield return Last;
实体配置:
public class JobConfiguration : IEntityTypeConfiguration<Job>
public void Configure(EntityTypeBuilder<Job> builder)
builder.OwnsOne(p => p.HiringManagerName, p =>
p.Property(pp => pp.First)
.IsRequired()
.HasColumnName("HiringManagerFirstName")
.HasMaxLength(200);
p.Property(pp => pp.Last)
.IsRequired()
.HasColumnName("HiringManagerLastName")
.HasMaxLength(200);
);
这被创建为表中的两列,就像Job Entity
中的其他列一样。
由于它也像实体中的其他属性一样创建为列,因此可以直接将其添加为作业实体中的普通属性。为什么需要将其添加为自有实体?
谁能帮我理解,
-
什么是自有实体?
为什么我们需要使用自有实体?
何时使用自有实体?
【问题讨论】:
【参考方案1】:没有自有实体会是什么样子?
如果您在 EF Core 中创建一个实体 Job
,该实体在其中一个属性中指向一个复杂对象 HiringManagerName
,则 EF Core 将期望每个实体都驻留在一个单独的表中,并期望您定义它们之间的某种关系(例如一对一、一对多等)。
在检索Job
时,如果您还想显式加载HiringManagerName
的值,则必须在查询中使用显式Include
语句,否则将不会被填充。
var a = dbContext.Jobs
.Include(b => b.HiringManagerName) //Necessary to populate
.ToListAsync();
但由于每个实体都被认为是一个单独的实体,因此它们将被要求公开键,并且您必须在每个实体之间配置外键。
什么是自有实体?
这就是[Owned]
类型的用武之地(参见docs)。通过使用 [Owned]
属性标记子类,您可以将该关系的显式处理留给 EF Core 来管理,并且不再需要在拥有的类型上定义键/外键。如果您指向您拥有的类型的集合,则相同 - 您不再需要处理任一类的导航属性来描述关系。
EF Core 还支持对这些拥有的类型进行查询,如下所示:
var job = context.Jobs.Where(a => a.HiringManagerName.First == "fingers10").FirstOrDefaultAsync();
现在,它带有两个重要的design restrictions 文档中描述(但在此处详细说明):
您不能为自有类型创建 DbSet这意味着您不能随后通过以下方式进行数据库调用:
dbContext.HiringManagerNames.ToListAsync();
这会抛出,因为您需要简单地检索值作为调用的一部分:
dbContext.Jobs.ToListAsync();
与我给出的第一个示例不同,HiringManagerNames
不再需要显式包含,而是通过调用 Jobs DbSet<T>
返回。
ModelBuilder
上的自有类型调用 Entity<T>
同样,您不能在模型构建器中引用您拥有的类型来配置它。相反,如果您必须配置它,请通过针对您的 Jobs 实体和拥有的属性的配置来执行此操作,例如:
modelBuilder.Entity<Job>().OwnsOne(a => a.HiringManagerNames).//Remaining configuration
那么我什么时候应该使用自有实体?
如果您的类型只会作为另一种类型的导航属性出现(例如,您永远不会将其本身作为查询的根实体进行查询),请使用拥有的类型以保存自己一些关系样板。
如果您期望独立于父实体查询子实体,请不要将其设为所有实体 - 需要使用自己的 DbSet<T>
定义它才能从上下文中调用。
【讨论】:
【参考方案2】:虽然@Whit Waldo 对技术 ef 核心的解释很好,但我们也应该尝试从Domain Driven Design 的角度来理解。
让我们观察问题本身中提到的类
public class Job : Entity
和
public class HiringManagerName : ValueObject
在 Entity 和 ValueObject 处做笔记。两者都是 DDD 概念。
身份对实体很重要,但对值对象无关。
看看this write up from Vladimir Khorikov for a more extensive explanation。
我在这里跳过了摘要项目符号。
实体有自己的内在身份,值对象没有。
身份平等的概念是指实体;结构平等的概念是指价值对象;引用相等的概念是指两者。
实体有历史;值对象的生命周期为零。
一个值对象应该始终属于一个或多个实体,它不能独立存在。
值对象应该是不可变的;实体几乎总是可变的。
要识别域模型中的值对象,请将其替换为整数。
值对象不应在数据库中拥有自己的表。
在您的域模型中,总是更喜欢值对象而不是实体。
因此,值对象由实体拥有。那么我们如何使用 EF Core 来实现呢?这里出现了拥有实体的概念。现在回去阅读@Whit Waldo 的答案。
【讨论】:
以上是关于什么是自有实体?何时以及为什么在 Entity Framework Core 中使用 Owned Entity?的主要内容,如果未能解决你的问题,请参考以下文章
Entity Framework 何时打开和关闭数据库连接?
Java SE 上的 JPA:对象:entity.Customer@5e80188f 不是已知的实体类型