首页 > 解决方案 > Group Join 和 Orderby,同时保持之前的查询

问题描述

在处理一个问题时,我意识到我需要查询表“问题”中的数据依赖于另一个表“Upvotes”中主键“questions.Id”的计数,其中“questionId”是“问题”的外键。 Id”并且可以有多个条目。

所以我的系统是如何工作的,我将赞成票条目添加到赞成票表中,我可以简单地计算(特定问题 ID)和问题总数。

我一直在弄清楚如何获取所有问题的列表及其各自的赞成票。

示例问题表:

 id(pk)             question         
  1                 "first quues"
  2                 "second ques"
  3                 "third ques"

投票表示例:

id(pk)             questionid           userid(user who upvoted)
  1                    2                   "alpha"
  2                    2                   "charlie"
  3                    1                   "bravo"
  4                    2                   "alpha"

预期输出:

id(question.id)      question              upvotecount
   2                   second ques             3
   1                   first  ques             1
   3                   third  ques             0  

(注意顺序和数量)

到目前为止我尝试过的查询:
最接近我的输出但需要存储在单独的变量中:

var t = query
    .Select(x => new
    {  
        question = x.Id,
        count = (from upvotes2 in model.Upvotes
                 where upvotes2.QuestionId == x.Id
                 select upvotes2).Count()
     })
    .OrderByDescending(c => c.count);

foreach (var a in t)
    Console.WriteLine("" + a);

我想做什么

query = query
    .Select(x => new Question
    {  
        UpvoteCount = (from upvotes2 in model.Upvotes
                       where upvotes2.QuestionId == x.Id
                       select upvotes2).Count()
    })
    .OrderByDescending(c => c.UpvoteCount);

foreach (var a in query)
    Console.WriteLine("" + a);

后者给了我:

System.NotSupportedException: 'mYpROEJT.DataAccess.CodeFirstModel.Question无法在 LINQ to Entities 查询中构造实体或复杂类型。

而前者接近输出,即:

“查询”的类型是IQueryable<Question> “问题”是从实体生成的一个类,我在[NotMapped] int UpvoteCount那里添加了一个

{ question = 5, upcount = 2 }
{ question = 3, upcount = 1 }
{ question = 2, upcount = 0 }
{ question = 1, count = 0 }
{ question = 4, count = 0 } 

编辑1:添加到原始帖子。我想返回问题列表和 Upvote 计数

标签: c#sqlentity-frameworklinq

解决方案


所以你有一个集合Questions。每个Question都有零个或多个Upvotes。每一个都Upvote属于一个Question,使用外键QuestionId。这是标准的一对多关系。

Users 和 Upvotes 之间也存在一对多的关系:每个 User 都有零个或多个 Upvote,每个 Upvote 都使用外键完全属于一个用户。

如果您使用实体框架代码优先约定,您将设计这些类,类似于以下内容:

public class Question
{
    public int Id {get; set;}

    // every Question has zero or more Upvotes:
    public virtual ICollection<Upvote> Upvotes {get; set;}

    public string QuestionText {get; set;}
    ... // other properties and relations
}
public class Upvote
{
    public int Id {get; set;}

    // every Upvote belongs to exactly one Question using foreign key:
    public int QuestionId {get; set;}
    public virtual Question Question {get; set;}

    // every Upvote was done by exactly one User using foreign key:
    public int UserId {get; set;}
    public virtual User User {get; set;}

    ... // other properties and relations
}
public class User
{
    public int Id {get; set;}

    // every User has zero or more Upvotes:
    public virtual ICollection<Upvote> Upvotes {get; set;}

    ... // other properties and relations
}

为了 DbContext 的完整性:

public class MyDbContext : DbContext
{
    public DbSet<Question> Questions {get; set;}
    public DbSet<UpVote> Upvotes {get; set;}
    public DbSet<User> Users {get; set;}
}

因为我坚持实体框架代码优先约定,所以这就是实体框架检测一对多关系所需知道的全部内容。您可能希望表或列有不同的标识符。在这种情况下,您需要使用流畅的 API 或属性。

以正确的实体框架方式设计您的类使您的查询变得非常简单和直观:

using (var dbContext = new MyDbContext())
{
    var result = dbContext.Questions      // take your table Questions
        .Select(question => new           // for every question in this table, make one new object
        {
             Id = question.Id,
             Text = question.QuestionText,
             UpvoteCount = question.Upvotes.Count,
        });
}

因为我使用的是virtual ICollection<Upvote> Upvotes,实体框架知道Questions表和Upvotes表之间需要一个 GroupJoin。在 Sql 中,这将成为一个inner join后跟一个group by.

如果您选择偏离标准的实体框架一对多设计,则不能使用ICollection.Count,您必须自己进行 GroupJoin:

var result = dbContext.Questions.GroupJoin(  // GroupJoin Questions with Upvotes
     dbContext.Upvotes,
     question => question.Id,                // from every question take the Id,
     upVote => upvote.QuestionId,            // from every upvote take the QuestionId,
     (question, upvotes) => new              // for every question with its matching Upvotes
     {                                       // make one new object
          Id = question.Id,
          Text = question.QuestionTxt,
          UpvoteCount = upvotes.Count(),
     });

最后,您想通过减少 UpvoteCount 来订购:

var finalResult = result.OrderByDescending(joinedItem => joinedItem.UpvoteCount);

推荐阅读