使用LINQ左外连接 - 理解代码

时间:2014-11-21 10:18:06

标签: c# sql linq

如果有人在使用 LINQ 时可以解释术语into的含义,我将不胜感激。一般来说,我试图了解如何在 C#中进行 INNER JOIN LEFT OUTER JOIN 等。

我有一个主表Students,它存储了一些外键ID,然后在运行查询时用它们的名字替换它们。这些名称是从MarksSoftwareVersionsDepartments等查找表中读取的。所有字段都是必填字段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;

在阅读了大量文章并观看了一些视频之后,我设法让下面的代码得到了解决,但我不觉得我对代码有完整的理解。令我困惑的是第5行以into结尾,然后是以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 --"
             };

2 个答案:

答案 0 :(得分:4)

我相信来自How to: Perform Left Outer Joins MSDN page示例部分已得到很好的解释。让我们把它投射到你的例子中。引用页面第一段

  

生成两个集合的左外连接的第一步是   使用组连接执行内部联接。 (参见如何:执行   内部连接(C#编程指南)对此进行了解释   process。)在此示例中,Person对象列表是内部连接的   到基于匹配的Person对象的Pet对象列表   Pet.Owner。

因此,在您的情况下,第一步是根据Students对象中Marks的{​​{1}}对象列表,执行MarkID个对象列表的内部联接匹配Students对象中的MarkID。从引用中可以看出,内部联接正在使用group join执行。如果您在MSDN页面中查看有关如何执行群组加入的Marks部分,则可以看到

  

第一个集合的每个元素都出现在a的结果集中   无论是否在相关元素中找到,都可以进行组连接   第二集。 如果找不到相关元素,   该元素的相关元素序列为空 。该   因此,结果选择器可以访问第一个元素   采集。

在您的示例的上下文中,这意味着使用Note您有into个结果,其中包含所有group joined个对象,以及{{1的相关元素序列对象(如果没有匹配的Students对象,则序列将为空)。

现在让我们回到Marks,特别是第二段

  

第二步是包括第一个(左)的每个元素   结果集中的集合,即使该元素没有匹配项   正确的收藏。这是通过调用DefaultIfEmpty来完成的   来自组连接的每个匹配元素序列。在这   例如,在匹配Pet的每个序列上调用DefaultIfEmpty   对象。该方法返回一个包含单个的集合,   如果匹配Pet对象的序列对于任何对象为空,则为默认值   Person对象,从而确保表示每个Person对象   在结果集合中。

同样,要将此项目投射到您的示例中,将在匹配的Marks个对象的每个序列上调用How to: Perform Left Outer Joins MSDN page。如上所述,如果匹配DefaultIsEmpty()对象的序列对于任何Marks对象为空,则该方法返回包含单个默认值的集合,这将确保每个Marks对象将被表示在最终的集合中。因此,您拥有的元素集包含所有Student个对象和匹配的Student对象,或者如果没有匹配的Student对象,则默认值为{{1} },在本例中为Marks

答案 1 :(得分:1)

我能说的是"进入MarksGroup"将连接表的结果数据存储到临时(基于应用程序,而不是基于数据库)结果集中(以sql术语表示:表格,因此其为SELECT INTO

在下一行中,您的代码会从Marksgroup中选择包含您数据的列(以sql术语:SELECT student, department, software, status, marked FROM Marksgroup

所以基本上,它从数据库中获取数据,然后将其放在" Marksgroup中,并在下一步中让Marksgroup回到您的手中以取出您想要使用的数据在你的c#代码中。

尝试摆脱Marksgroup,它应该是可能的(没有经过测试的代码)。它应该是这样的:

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' :如果没有临时结果集,这也应该显示不同的行为" Marksgroup"