什么是自有实体?何时以及为什么在 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&lt;T&gt; 返回。

无法使用 ModelBuilder 上的自有类型调用 Entity&lt;T&gt;

同样,您不能在模型构建器中引用您拥有的类型来配置它。相反,如果您必须配置它,请通过针对您的 Jobs 实体和拥有的属性的配置来执行此操作,例如:

modelBuilder.Entity<Job>().OwnsOne(a => a.HiringManagerNames).//Remaining configuration

那么我什么时候应该使用自有实体?

如果您的类型只会作为另一种类型的导航属性出现(例如,您永远不会将其本身作为查询的根实体进行查询),请使用拥有的类型以保存自己一些关系样板。

如果您期望独立于父实体查询子实体,请不要将其设为所有实体 - 需要使用自己的 DbSet&lt;T&gt; 定义它才能从上下文中调用。

【讨论】:

【参考方案2】:

虽然@Whit Waldo 对技术 ef 核心的解释很好,但我们也应该尝试从Domain Driven Design 的角度来理解。

让我们观察问题本身中提到的类

public class Job : Entity

public class HiringManagerName : ValueObject

EntityValueObject 处做笔记。两者都是 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 不是已知的实体类型

什么时候用Model,什么时候用Entity?[转载知乎-备忘]

Spring JPA 中的@Entity 是啥?

.AsExpandable 在 Linq to Entity