EF core一对多关系,HasOne().WithMany()与HasMany().WithOne()的区别

28

假设我有以下2个模型:

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog { get; set; }
}

现在,如果我想在DbContext中配置模型关系,以下两种方式有什么区别:

modelBuilder.Entity<Post>()
            .HasOne(p => p.Blog)
            .WithMany(b => b.Posts);

modelBuilder.Entity<Blog>()
            .HasMany(b => b.Posts)
            .WithOne(p => p.blog);

如果有区别的话,它是什么?我应该写两个还是只写一个?

另外一件事: 我需要定义外键吗?根据我对数据库的了解,你不能在没有外键的情况下创建关系,但是EF不要求你拥有外键字段。那么EF如何处理不知道外键的关系?会导致性能下降或错误吗?


1
今天可能会有所帮助:https://www.learnentityframeworkcore.com/configuration/one-to-many-relationship-configuration#inverse-navigation-property - DefinedRisk
2个回答

13

没错,您可以在DbContext中创建没有数据库外键的关系。

此外:

WithOne: 一对一关系在两侧都有引用导航属性。它们遵循与一对多关系相同的约定,但会在外键属性上引入唯一索引以确保每个主体只与一个从属关联。

Many-to-many: 没有实体类来表示连接表的多对多关系尚未得到支持。但是,您可以通过包括一个连接表的实体类并映射两个单独的一对多关系来表示多对多关系。

您只需要定义一个关系,因为在某些情况下,您将创建没有导航属性(单个或集合)的父子关系。

对于您的示例:您为Blog->Posts添加了一条关系,因为这两个对象都具有导航属性,在不同的方式下完成了同样的两行:

  • Blog->Posts(父->子)
  • Posts->Blog(子->父)

2
那么您是在说这两种方法之间没有区别,无论哪种方式我都可以以相同的方式使用模型吗?但是使用HasOne()和WithMany()更好一些? - Soorena Aban
在这种情况下,没有更好的方法,它们只是创建两个对象之间关系的不同方式,通常映射是从子级到父级。 - H. Herzl
4
可以在DbContext中创建关系,而不需要在数据库中使用外键。但这是不正确的。Entity Framework将引入一个阴影属性,该属性不会出现在您的模型中,但会出现在数据库中。 - Knelis
谢谢您的反馈,无论如何,我是指从应用程序方面来看,我们并不关心数据库中是否存在该关系的约束条件,有时会有人问我们是否需要先从数据库方面定义关系。 - H. Herzl
@H.Herzl 我担心EFCore确实做出了这样的假设,即数据库中总是存在约束条件,因为当我们使用.OnDelete(DeleteBehavior.Cascade)配置关系时,该行为仅处理已跟踪的依赖实体,而未跟踪的实体只能通过级联删除外键来神奇地处理。 请参阅此文章并注意下面的评论: https://learn.microsoft.com/en-us/ef/core/saving/cascade-delete 在我看来,让ORM对数据库结构和约束施加某些期望是一个非常短视的设计决策。 - Alex Lobakov
针对多对多关系的特性请求,无需使用实体类来连接表,请参见“讨论多对多关系(无 CLR 连接表类)”https://github.com/aspnet/EntityFrameworkCore/issues/1368。 - Elijah Lofgren

4
您可以定义没有外键属性的模型。但是,Entity Framework将引入一个影子属性,它将存在于数据库中。
根据文档:
尽管建议在从属实体类中定义外键属性,但这不是必需的。如果没有找到外键属性,则会引入一个影子外键属性,其名称为<navigation property name><principal key property name>(有关更多信息,请参见影子属性)。

它不会“在数据库中”,而是在EF Core的实体定义中。 - Doug
1
它肯定会在数据库中。根据文档:当数据库中存在不应在映射实体类型上公开的数据时,影子属性非常有用。它们最常用于外键属性,其中两个实体之间的关系由数据库中的外键值表示,但是关系是通过实体类型之间的导航属性来管理的。 - Knelis
影子属性是一个Entity Framework的概念,而不是数据库的概念。因此,它们不在数据库中。我提到这一点只是为了给任何研究此主题的人带来清晰度。 - Doug
2
实际上,如果您尝试我的先前评论中链接的EF Core文档中的示例,您会发现EF将在“帖子”表上“在数据库中”创建一个名为BlogId的列。也许我们谈论的是不同的事情,但是影子属性肯定会在数据库中有相应的列。 - Knelis
5
我认为“影子属性”指的是你在类型本身上看不到该属性,但它会存在于数据库中。 - Saeb Amini

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接