【问题标题】:Many-to-many query in Entity Framework 7Entity Framework 7 中的多对多查询
【发布时间】:2016-08-12 01:17:57
【问题描述】:

我正在关注我从http://ef.readthedocs.org/en/latest/modeling/relationships.html得到的这个例子

class MyContext : DbContext
{
    public DbSet<Post> Posts { get; set; }
    public DbSet<Tag> Tags { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<PostTag>()
            .HasKey(t => new { t.PostId, t.TagId });

        modelBuilder.Entity<PostTag>()
            .HasOne(pt => pt.Post)
            .WithMany(p => p.PostTags)
            .HasForeignKey(pt => pt.PostId);

        modelBuilder.Entity<PostTag>()
            .HasOne(pt => pt.Tag)
            .WithMany(t => t.PostTags)
            .HasForeignKey(pt => pt.TagId);
    }
}

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

    public List<PostTag> PostTags { get; set; }
}

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

    public List<PostTag> PostTags { get; set; }
}

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

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

现在我的问题是如何构建我的查询来获取给定 TagId 的帖子?比如:

public List<Post> GetPostsByTagId(int tagId)
{
    //linq query here
}

请记住这是 EF7。

【问题讨论】:

    标签: c# entity-framework entity-framework-core


    【解决方案1】:

    这并不是 EF7 所特有的。

    您可以扩展您的 DbContext 以包含 PostTags

    class MyContext : DbContext
    {
        public DbSet<PostTags> PostTags { get; set; }
    

    然后你的查询

    db.Posts.Where(post => db.PostTags.Any(pt => pt.PostId == post.PostId && pt.TagId == tagId))
    .Select(post => post);
    

    【讨论】:

      【解决方案2】:
      db.Posts.Where(post => post.PostTags.Any(pt => pt.TagId == tagId));
      

      【讨论】:

      • 这可行并且可能是最简单的解决方案,但是查看 sql 分析器,这使用 CASE 语句和嵌套选择创建了一个相当复杂且效率低下的查询。不知道为什么...
      • @WheelBuilder 因为 EF Core 仍然缺乏正确的 LINQ 到 SQL 转换,并且非直接前向查询不会转换为 SQL,因此在客户端的内存中执行 N 个更简单的更简单的 SQL 查询。例如,目前不支持 GROUP BY 的情况与 SQL 转换相同(它在内存中对表中的每个条目进行简单的 SQL 查询),并且只希望在 EF Core v1.1 中实现而不是完全实现。
      • 只有这个解决方案允许我获取帖子的子对象(标签)。但是你需要用 Includes 重写它。
      【解决方案3】:

      我的第一个建议是将您的收藏属性更改为 ICollection&lt;T&gt; 而不是 List&lt;T&gt;。您可以在 post 中找到非常好的解释。

      现在回到你真正的问题,这就是我会如何做你的查询:

      public List<Post> GetPostsByTadId(int tagId)
      {
          using(var context=new MyContext())
          {
            return context.PostTags.Include(p=>p.Post)
                                   .Where(pt=> pt.TagId == tagId)
                                   .Select(pt=>pt.Post)
                                   .ToList();
          }
      }
      

      您需要预先加载 Post 导航属性,因为 EF7 不支持延迟加载,而且,正如 @Igor 在他的解决方案中建议的那样,您应该在上下文中包含 PostTags 作为 DbSet

       public DbSet<PostTags> PostTags { get; set; }
      

      说明:

      您的查询从PostTags 表开始,因为在该表中您可以找到与特定标签相关的所有帖子。请参阅Include,例如与Post 表的内部连接。如果您在PostTagsPosts 之间应用连接,按TagId 过滤,您将获得所需的列。通过Select 调用,您告诉您只需要Post 表中的列。

      如果您删除 Include 调用,它应该仍然有效。使用Include,您明确告诉您需要进行连接,但是使用Select,EF 的Linq 提供程序足够聪明,可以看到它需要进行隐式连接以获得Posts 列结果。

      【讨论】:

      • 不确定它是如何工作的,因为我是 EF 的新手,但是这个查询创建了最有效的 SQL 语句:PostTags INNER JOIN Posts... 其他答案导致绝对可怕的嵌套 SELECTs 语句:SELECT from Posts WHERE (SELECT CASE WHEN EXISTS ( SELECT 1 FROM PostTags... 你能解释一下发生了什么?
      【解决方案4】:

      include theinclude 都可以正常工作...但唯一的问题是智能感知无法识别或显示方法,只需键入然后继续所有工作正常...

                  var res = await _context.Diseases.Include(x => x.Disease2Symptom)
                  .ThenInclude(z => z.Symptom).ToListAsync();
      

      【讨论】:

      • 这没有回答问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-28
      • 1970-01-01
      • 1970-01-01
      • 2021-07-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多