C# 模拟使用与等于相反的contains运算符的联接?
我发现了join操作符,因此。但是,我需要执行非等联接 我特别需要使用以下设置编写一个查询。给定两种类型的对象类和学生 我想这样写查询C# 模拟使用与等于相反的contains运算符的联接?,c#,linq,iqueryable,C#,Linq,Iqueryable,我发现了join操作符,因此。但是,我需要执行非等联接 我特别需要使用以下设置编写一个查询。给定两种类型的对象类和学生 我想这样写查询 var query = from cls in allClasses join std in allStudents on cls.Students.Contains(std.StudentId) into clsStds where clsStds.Select(aStd => aStd.Grade).Average() > 70
var query = from cls in allClasses
join std in allStudents on cls.Students.Contains(std.StudentId) into clsStds
where clsStds.Select(aStd => aStd.Grade).Average() > 70
select cls;
虽然这显然是无效的语法。提供了一个非等联接的示例,尽管我尝试在这里应用它,但似乎无法正确地复制它和/或我自己严重困惑
如何模拟上面描述的联接类型?您使用的是实体框架还是类似的?实体之间是否有导航属性?如果是这样,也许您可以对学生进行分组,然后导航到父对象。比如:
var query = allStudents
.GroupBy(i=>i.Class)
.Select(i=>new{
Class = i,
Average = i.Average(j=>j.Grade)
})
.Where(i=>i.Average > 70)
.ToList();
我找到了一种解决这个问题的简单方法。首先,我们将student中的Guid引用解析为实际的student对象 然后我们可以查询这个匿名对象集合
var classes =
from clsStdObj in clsStd
where clsStdObj.stdObjs.Select(stdObj => stdObj.Grade).Average() > 70
select clsStdObj.cls;
然后将其分解为学生平均成绩在70分以上的所有班级
但我仍然愿意接受不那么幼稚的解决方案。这可能是一个解决方案,这要感谢@EricLippert提供了使用Any的建议,以避免classGrades为空时可能出现的崩溃:
var query = from _class in allClasses
let classGrades = from std in allStudents
where _class.Students.Contains(std.StudentId)
select std.Grade
where classGrades.Any()
where classGrades.Average() > 70
select _class;
基本上,对于每个类,我创建一个新的子查询,在其中选择该类中的所有学生,并仅预测成绩,因此结果是IEnumerable。下一部分很简单:平均分数
出于好奇,我使用秒表将您的解决方案与我的解决方案进行了比较,即使它纯粹是指示性的,我/您的延迟时间之间的比率约为0.02。首先,您的数据模型是错误的。学生没有一个年级。他们在一个班级里有一个等级,而你的模型没有考虑到这一点。您需要第三个表,包含student、class和grade列。我强烈建议你解决这个问题 解决上述问题很简单,但我不喜欢目前为止提出的任何解决方案。它们基本上是合理的,但可能更高效、更健壮 你所面临的基本问题是:你没有一个快速简单的方法从学生ID到学生对象。首先解决这个问题: 太好了。现在,解决方案很简单:
var query =
from cls in allClasses
let grades = from id in cls.Students select idToStudent(id).Grade
where grades.Any()
where grades.Average() > 70
select cls;
请注意,我们正在测试是否有任何分数,因为可能会有一个没有学生的班级。如果要求平均值为零,则平均值将崩溃,因此检查是明智的
当您修复数据模型以便正确关联学生、班级和成绩时,它将是:
var query =
from cls in allClasses
let grades =
from grade in allGrades
where grade.Class == cls
select grade.grade
where grades.Any()
where grades.Average() > 70
select cls;
在一组设计合理的表格中,学生ID在计算平均值时不会出现在其中;你可以把成绩和班级直接联系起来
现在,您在开始这个问题时注意到您需要一种C不支持的连接。不,您需要修复数据关系,然后C支持您需要的连接类型!以上内容可以更有效地写成
var query =
from cls in allClasses
join g in allGrades on cls equals g.Class into grades
where grades.Average() > 70
select cls;
由于C不会生成空组,因此检查任何组的需要消失了
这就是你需要的加入;正确设计您的桌子,然后使用它 我不熟悉实体框架,我以前在很多地方见过它,但我不确定它是什么或用于什么。这个问题来自于试图以问题中指定的方式查询MongoDB。啊,我明白了。实体框架是用于数据层的ORM,除此之外,还为数据存储中的对象生成类。它很方便,因为它允许您以面向对象的方式使用数据层,而无需花费大量精力。您有没有办法在模型中从一个学生到另一个班级?从一个学生到另一个班级是没有办法的。只有类通过Guid id引用学生,学生甚至不知道它在一个类中。因此,您只想要所有类中成绩高于70的学生?我想要相反的,我想要所有学生平均成绩高于70的类。您将问题标记为IQueryable,但您似乎正在使用LINQ对象;你能解释一下为什么要贴标签吗?我给出的查找解决方案假定LINQ是对象,IQueryable来自于MongoDB中LinqExtension的使用。这个问题代表了一个更简单和类似的情况,我希望在MongoDB中构建一个查询,因此我有您在回答中提到的奇怪的数据模型。对不起,IMongoQueryable,我刚刚意识到它的Mongo特定用法。这是一个合理的解决方案尝试,但有几个问题。如果有一个班没有学生,那么平均崩溃;您可以在其中添加classGrades.Any。第二,当班上已经有一个成员学生的列表时,迭代所有学生并拉出班上的学生,这有点奇怪@EricLippert谢谢你让我想起了那次普通的车祸,我没想过。然而关于第二点,我知道这很奇怪,但我选择了给他一个解决方案 基于他的模型和约束条件。我读了你的答案,你是绝对正确的,因为你强调了这背后的问题。
var idToStudent = allStudents.ToLookup(s => s.id);
var query =
from cls in allClasses
let grades = from id in cls.Students select idToStudent(id).Grade
where grades.Any()
where grades.Average() > 70
select cls;
var query =
from cls in allClasses
let grades =
from grade in allGrades
where grade.Class == cls
select grade.grade
where grades.Any()
where grades.Average() > 70
select cls;
var query =
from cls in allClasses
join g in allGrades on cls equals g.Class into grades
where grades.Average() > 70
select cls;