使用 LINQ 的左外部联接 -- 理解代码
本文关键字:代码 LINQ 外部 使用 | 更新日期: 2023-09-27 18:31:32
如果有人能在使用 LINQ 时解释术语into
的含义,我将不胜感激。一般来说,我试图了解如何在 C# 中进行内连接、左外部连接等。
我有一个主表Students
它存储了一些外部 ID 键,然后在运行查询时用它们的名称替换这些键。名称是从查找表中读取的,例如Marks
、SoftwareVersions
、Departments
等。所有字段均为必填项,但MarkID
.我尝试在 LINQ 中构建的查询是这样的:
SELECT * FROM dbo.Students
INNER JOIN dbo.Departments ON dbo.Students.DepartmentID=dbo.Departments.DepartmentID
INNER JOIN dbo.SoftwareVersions ON dbo.Students.SoftwareVersionID=dbo.SoftwareVersions.SoftwareVersionID
INNER JOIN dbo.Statuses ON dbo.Students.StatusID=dbo.Statuses.StatusID
LEFT JOIN dbo.Marks ON dbo.Students.MarkID=dbo.Marks.MarkID
WHERE dbo.Students.DepartmentID=17;
在阅读了大量文章并观看了一些视频后,我不知何故设法使下面的代码正常工作,但我觉得我对代码没有完全的理解。让我感到困惑的部分是在以 into
结尾的第 5 行,然后在以 from m ...
开头的下一行。我很困惑into
做什么以及from m ...
中到底发生了什么.这是 LINQ 中的代码:
var result = from st in dbContext.Students where st.DepartmentID == 17
join d in dbContext.Departments on st.DepartmentID equals d.DepartmentID
join sv in dbContext.SoftwareVersions on st.SoftwareVersionID equals sv.SoftwareVersionID
join stat in dbContext.Statuses on st.StatusID equals stat.StatusID
join m in dbContext.Marks on st.MarkID equals m.MarkID into marksGroup
from m in marksGroup.DefaultIfEmpty()
select new
{
student = st.StudentName,
department = p.DepartmentName,
software = sv.SoftwareVersionName,
status = st.StatusName,
marked = m != null ? m.MarkName : "-- Not marked --"
};
我相信"如何:执行左外部联接 MSDN"页面中的示例部分得到了很好的解释。让我们将其投影到您的示例中。引用页面中的第一段
生成两个集合的左外连接的第一步是 使用组联接执行内部联接。(请参见如何:执行 内部联接(C# 编程指南)对此进行了说明 过程。在此示例中,Person 对象列表是内部联接的 到基于匹配的 Person 对象的宠物对象列表 宠物主人。
因此,在您的情况下,第一步是执行Students
对象列表与基于MarkID
对象匹配Marks
对象中的MarkID
Marks
对象列表的内部联接Students
对象。从报价中可以看出,内部联接是使用组联接执行的。如果检查 MSDN 页面中有关如何执行组加入Note
部分,则可以看到
在您的第一个集合的每个元素都出现在 组联接,无论是否在 第二个集合。在未找到相关元素的情况下, 该元素的相关元素序列为空。这 因此,结果选择器可以访问第一个的每个元素 收集。
示例上下文中,这意味着通过使用into
,您可以获得group joined
结果,其中您拥有所有Students
对象,以及Marks
对象的相关元素序列(如果没有匹配Marks
对象,则序列将为空)。
现在让我们回到How to: Perform Left Outer Joins MSDN page
,特别是第二段
第二步是包含第一个元素的每个元素(左) 结果集中的集合,即使该元素在 正确的集合。这是通过调用 DefaultIfEmpty 来实现的 在组联接的每个匹配元素序列上。在此 例如,在匹配宠物的每个序列上调用 DefaultIfEmpty。 对象。该方法返回一个集合,其中包含单个, 默认值,如果匹配的 Pet 对象的序列对于任何 Person 对象,从而确保表示每个 Person 对象 在结果集合中。
同样,为了将其投影到您的示例中,将在每个匹配Marks
对象序列上调用DefaultIsEmpty()
。如上所述,如果任何Student
对象的匹配Marks
对象的序列为空,该方法将返回一个包含单个默认值的集合,这可确保每个Student
对象都将在生成的集合中表示。结果,您拥有的是一组元素,其中包含所有Student
对象,并且Marks
对象匹配,或者如果没有匹配Marks
对象,则默认值为 Marks
,在本例中为 null
。
说的是,"into MarksGroup"将连接表的结果数据存储到临时(基于应用程序,而不是基于数据库)结果集中(用SQL术语来说:一个表,所以它是一个SELECT INTO
)
在下一行中,代码然后从 Marksgroup 中选择包含数据的列(以 sql 术语表示:SELECT student, department, software, status, marked FROM Marksgroup
所以基本上,它从数据库中获取数据,然后将其放在"Marksgroup"一边,在下一步中让Marksgroup回到你的手指中,取出你想要在c#代码中使用的数据。
尝试摆脱Marksgroup,这应该是可能的(还没有用你的代码测试过ist)。它应该是这样的:
from st in dbContext.Students where st.DepartmentID == 17
join d in dbContext.Departments on st.DepartmentID equals d.DepartmentID
join sv in dbContext.SoftwareVersions on st.SoftwareVersionID equals sv.SoftwareVersionID
join stat in dbContext.Statuses on st.StatusID equals stat.StatusID
join m in dbContext.Marks on st.MarkID equals m.MarkID
select new
{
student = st.StudentName,
department = p.DepartmentName,
software = sv.SoftwareVersionName,
status = st.StatusName,
marked = m != null ? m.MarkName : "-- Not marked --"
};
你用"m"的第二个问题:这也应该在没有临时结果集"标记组"的情况下显示不同的行为