在实体框架中加入 3 个一对多表

Posted

技术标签:

【中文标题】在实体框架中加入 3 个一对多表【英文标题】:Join 3 One to Many Tables in Entity Framework 【发布时间】:2017-02-06 20:42:02 【问题描述】:

我有 2 个表,每个表与之间的表具有一对多关系,并且表之间的表具有 2 个其他表的 id

dbo.Posts          dbo.Posts_Categories          dbo.Categories
   -ID                -ID                           -ID
   -Title             -PostID                       -Name
                      -CategoryID

我期望的结果是:

Title = post1          Categories = web,mobile,desktop
Title = post2          Categories = app,game
...

我知道如何在 sql 中使用 Stuff 函数和 For Xml Path 进行查询,但我不知道如何在实体框架中执行此操作!

任何关于如何以这种方式工作的建议或书籍都可能有所帮助!

编辑:添加了 EF 类:

    public class Post : ReportingBase 
        public Post()  

        [Required, MaxLength(500)]
        public string Title  get; set; 
        [Required, MaxLength(500)]
        public string Address  get; set; 
        [Required]
        public string Body  get; set; 
        [Required, MaxLength(500)]
        public string Tags  get; set; 
        [Required]
        public int Visit  get; set; 



        public virtual ICollection<Post_Category> Posts_Categories  get; set; 
        public virtual ICollection<Post_AttachedFile> Posts_AttachedFiles  get; set; 

        [ForeignKey("Image")]
        public virtual int? ImageID  get; set; 
        public virtual Image Image  get; set; 
    

    public class Post_Category 
        public Post_Category()  

        [Key, Column(Order = 0)]
        public int PostID  get; set; 

        [Key, Column(Order = 1)]
        public int CategoryID  get; set; 

        public virtual Post Post  get; set; 
        public virtual Category Category  get; set; 
    

    public class Category : EntityBase 
        public Category()  

        [Required, MaxLength(50)]
        public string Name  get; set; 

        [Required, MaxLength(150)]
        public string Address  get; set; 
        public int? ParentID  get; set; 



        public virtual ICollection<Post_Category> Posts_Categories  get; set; 
    

提前谢谢你

编辑:根据@IvanStoev 的回答,我做了以下操作:

    List<P> p = context.Posts.Select(post => new 
        Title = post.Title,
        Categories = post.Posts_Categories.Select(pc => pc.Category.Name).ToList()
    ).ToList();

并创建了一个名为 P 的类:

public class P 
    public string Title  get; set; 
    public List<string> Categories  get; set; 

但它不能正常工作,问题是如何返回结果。

【问题讨论】:

好吧,为了帮助进行 EF 查询,我们需要 EF 模型(类、流式配置等)而不是 db 表。 我添加了实体@IvanStoev 您可以删除所有那些空的构造函数 - 只会使您的代码变得模糊。另外,在定义一对多关系时,建议使用ICollection&lt;Type&gt; 而不是List&lt;Type&gt; 【参考方案1】:

由于所谓的navigation properties 的概念,在 EF 中它甚至比在 SQL 中更容易。您只需要了解基本的 LINQ 查询语法,然后按照它们(导航)即可获取所需的数据。例如:

var result = db.Posts
    .Select(post => new
    
        Title = post.Title,
        Categories = post.Posts_Categories
            .Select(pc => pc.Category.Name)
            .ToList()
    )
    .ToList();

结果是具有string Title 属性和包含相关类别名称的List&lt;string&gt; Categories 属性的匿名类型列表。

【讨论】:

它似乎可以工作,但我不知道如何退货!我的意思是我不知道什么是结果类型@IvanStoev 只需创建一个类 - public class SomeClass public string Title get; set; public List&lt;string&gt; Categories get; set; 并使用 post =&gt; new SomeClass ... 。结果类型将为List&lt;SomeClass&gt;。啊,我看到了你的编辑 - 你快到了,忘了在 new 之后添加 P :) 这是错误的代码。你不应该像那样调用.ToList() 两次——它会导致两个单独的查询,如果你使用.Include() 的导航属性,你可以在查询中做到这一点。此外,EF 会正确索引您要加入的外键。否决先生 @KellenStuart 这是针对 EF Core 2.x 先生的。那时,EF Core 需要内部 ToList() 调用来选择应用优化以实际避免 N + 1 个查询。没有“单查询”模式,它是在 3.0 中引入的。没有它,他们用它执行了 N + 1 个查询——只有 2 或 3 个(类似于 5.x “拆分查询”模式:-) EF Core 团队每次发布都改变概念并打破现有的不是我的错以一种或另一种方式编码。 @IvanStoev 如果是这种情况,问题应该用 【参考方案2】:

您可以使用 Linqpad(软件)来熟悉 Linq 查询,它通过连接到数据库为您构建 lambda 表达式并提供输出以进行交叉验证。

下面的一个是用于连接您提到的表的 lambda 表达式。

p - Post  
pc - post_categories
c - categories

代码:

Posts.Join(Post_Categories, p => p.ID, pc => pc.ID, ( p, pc) => new  p = p, pc = pc)
     .Join(Categories, pcc => pcc.pc.CategoryID, c => c.ID, ( pcc, c) => new  pcc = pcc, c = c)
     .Select(p.Title)
     .Select(c.Name)

【讨论】:

【参考方案3】:

您应该使用.Include()任何加入 EF Core。

我想出了一个简单的例子:一个人可以养很多条狗

public class Person

    public Guid Id  get; set; 
    public ICollection<Dog> Dogs  get; set;  // One Person can have many Dogs


public class Dogs

    public Guid Id  get; set; 
    public Guid PersonId  get; set; 

创建模型后生成迁移。在这个答案中不讨论如何做到这一点。

以下是您如何使用.Include() 加入两个不同的表:

public class PersonRepository : RepositoryBase

    public IEnumerable<Person> FetchPeopleWithManyDogs()
    
        return DatabaseContext.Person
            .Include(x => x.Dogs)
            .Where(x => x.Dogs.Count() > 1).ToList();
    

【讨论】:

以上是关于在实体框架中加入 3 个一对多表的主要内容,如果未能解决你的问题,请参考以下文章

mybatismybatis多表联查,存在一对多关系的,实体中使用List作为字段接收查询结果的写法

Hibernate框架进阶(中篇)之多表关系

一对多关系不会在实体框架中检索数据

day06 多表查询

mybatis xml数据层框架应用--Mybatis关系映射之一对多关系映射

MyBatis关于多表联查 关联关系之一--------一对多(单条sql语句查询)