分组加入和排序,同时保持先前的查询

时间:2018-07-21 13:49:53

标签: c# sql entity-framework linq

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

所以我的系统是如何工作的,我将upvotes条目添加到upvote表中,我可以简单地计算(特定问题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:'无法在LINQ to Entities查询中构造实体或复杂类型mYpROEJT.DataAccess.CodeFirstModel.Question

而前者接近输出,即:

“查询”的类型为IQueryable<Question> “问题”是从Entity生成的类,我在其中添加了[NotMapped] int UpvoteCount

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

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

1 个答案:

答案 0 :(得分:0)

因此,您有Questions的集合。每个Question都有零个或多个Upvotes。每个Upvote都使用外键Question恰好属于一个QuestionId。这是标准的一对多关系。

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

如果您使用entity-framework code-first conventions,则将设计类似于以下内容的此类:

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);