【问题标题】:What is Owned Entity? When and why to use Owned Entity in Entity Framework Core?什么是自有实体?何时以及为什么在 Entity Framework Core 中使用 Owned Entity?
【发布时间】: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. 什么是自有实体?
  2. 为什么我们需要使用自有实体?
  3. 何时使用自有实体?

【问题讨论】:

    标签: entity-framework-core


    【解决方案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 的答案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-09-24
        • 2021-08-28
        • 2022-01-21
        • 2016-12-21
        • 1970-01-01
        • 2021-11-14
        • 2019-10-10
        • 2019-09-04
        相关资源
        最近更新 更多