【问题标题】:Querying many to many table in EF Core/LINQ [duplicate]在 EF Core/LINQ 中查询多对多表 [重复]
【发布时间】:2020-07-11 13:19:07
【问题描述】:

我有三个表:Posts、Tags 和 PostTags(Post 和 Tag 之间的链接表)。如何编写查询以通过 TagId 获取所有帖子?

数据库结构:

public class Post {
    public string Id {get;set;}
    public string Content {get;set;}
    public List<PostTag> PostTags {get;set;}
}

public class Tag {
    public string Id {get;set;}
    public string Name {get;set;}
    public List<PostTag> PostTags {get;set;}
}

public class PostTag
{
    public string PostId { get; set; }
    public Post Post { get; set; }

    public string TagId { get; set; }
    public Tag Tag { get; set; }
}

关系:

builder.Entity<PostTag>()
    .HasKey(x => new { x.PostId, x.TagId });

builder.Entity<PostTag>()
    .HasOne(st => st.Post)
    .WithMany(s => s.PostTags)
    .HasForeignKey(st => st.PostId);

builder.Entity<PostTag>()
    .HasOne(st => st.Tag)
    .WithMany(s => s.PostTags)
    .HasForeignKey(st => st.TagId);

【问题讨论】:

标签: linq entity-framework-core


【解决方案1】:

如果你关注了entity framework code first conventions,有两种方法可以查询“带有标签的帖子”

  • 简单的方法:使用virtual ICollection&lt;Tag&gt;获取每个帖子的标签。
  • 自己加入(小组)。

使用实际的 ICollection

您的课程将类似于以下内容:

class Post
{
    public int Id {get; set;}
    ... // other properties

    // every Post has zero or more Tags (many-to-many)
    public virtual ICollection<Tag> Tags {get; set;}
}

class Tag
{
    public int Id {get; set;}
    ... // other properties

    // every Tag is used by zero or more Posts (many-to-many)
    public virtual ICollection<Post> Posts {get; set;}
}

这就是实体框架需要知道帖子和标签之间的多对多关系的全部内容。您甚至不必提及联结表,实体框架会为您创建一个标准表,并在需要时使用它。仅当您想要表和/或列的非标准名称时,您才需要 Attributes 或 fluent API。

在实体框架中,表的列由非虚拟属性表示;虚拟属性表示表之间的关系(一对多,多对多,...)

要获取所有(或部分)帖子,每个帖子都有所有(或部分)他们的表格,您可以使用虚拟 ICollection:

var postsWithTheirTags = dbContext.Posts
    // only if you don't want all Posts:
    .Where(post => ...)

    .Select(post => new
    {
        // Select only the Post properties that you plan to use:
        Id = post.Id,
        Author = post.Author,
        ...

        Tags = post.Tags.Select(tag => new
        {
             // again: only the properties that you plan to use
             Id = tag.Id,
             Text = tag.Text,
             ...
        })
        .ToList(),
    });

实体框架知道您的关系,并会使用正确的连接表自动为您创建一个 Group-join。

在我看来,这个解决方案是最自然的。

自己做组加入

为此,您需要访问联结表,您必须在 dbContext 中提及它,并使用 fluent API 告诉实体框架这是帖子之间多对多关系的联结表和标签。

var postsWithTheirTags = dbContext.Posts.GroupJoin(dbContext.PostTags,

    post => post.Id,              // from every Post take the primary key
    postTag => postTag.PostId     // from every PostTag take the foreign key to Post

    (post, postTagsOfThisPost) => new
    {
        // Post properties:
        Id = post.Id,
        Title = post.Title,
        ...

        Tags = dbContext.Tags.Join(postTagsOfThisPost,

        tag => tag.Id                // from every Tag take the primary key
        postTag => postTag.TagId     // from every postTagOfThisPost take the foreign key

        (tag, postTagfThisPostAndThisTag) => new
        {
            Id = tag.Id,
            Text = tag.Text,
            ...
        })
        .ToList(),
    });

【讨论】:

  • Post 对象没有标签的导航属性,所以我不确定post.Tags 会如何工作。
  • 不知何故,您团队中的某个人能够说服您的项目负责人相信,偏离 EF 实现多对多关系的标准方法是一个好主意。要么让您的项目负责人认识到将代码还原为正确实现的重要性,要么坚持使用有偏差的代码。
  • 官方文档中的例子也遵循了和我一样的约定,即 Post 和 Tag 类只引用了链接表 PostTag docs.microsoft.com/en-us/ef/core/modeling/…
  • 好吧,如果没有理由偏离它们,也许你仍然及时提出遵循约定。如果不行,请考虑创建一个扩展函数来查询“带有标签的帖子”和“带有帖子的标签”,这会隐藏您的内部数据结构。如果您返回 IQueryable&lt;...&gt;,那么其他人可以在他们的 LINQ 查询中使用这些方法。
  • 仍然不确定约定的主题来自哪里,因为我确实遵循约定。但是感谢扩展方法的想法,我可以看到它很有用。
【解决方案2】:

你可以试试这个:

public List<Posts> GetPosts(string needTagID)
{
    var dataQuery = from tags in _db.Tags
                    where needTagID == tags.Id
                    join postTags in _db.PostTags on tags.Id equals postTags.TagId
                    join posts in _db.Posts on postTags.PostId equals posts.Id
                    select posts;
    
    var data = dataQuery.ToList();
}

【讨论】:

  • 我不是在查询标签 ID tagsIDsList 的列表,而是一个。我想要所有具有提供的 TagId 的帖子。
  • 好的,我更正了。
  • 你能解释一下吗?
  • SQL 将执行两个连接操作。这将获得所有表连接。接下来,必要条件needTagID == tags.TagID就会满足。
  • 帖子没有引用 TagId 所以posts.TagID 将不起作用
猜你喜欢
  • 2021-10-21
  • 1970-01-01
  • 1970-01-01
  • 2019-12-17
  • 2019-01-19
  • 2021-12-27
  • 1970-01-01
  • 2017-11-13
  • 2019-03-27
相关资源
最近更新 更多