具有第一个或默认连接的Linq查询

时间:2016-03-15 20:03:08

标签: c# entity-framework linq linq-query-syntax

我有以下数据模型:

public class Course
{
    public int CourseId { get; set; }
    public int StateId { get; set; }
}

public class CompletedCourse
{
    public int CompletedCourseId { get; set; }
    public int UserId { get; set; }
    public Course Course { get; set; }
    public string LicenseNumber { get; set; }
}

public class License
{
    public int LicenseId { get; set; }
    public int UserId { get; set; }
    public int StateId { get; set; }
    public string LicenseNumber { get; set; } 
}

我正在努力为IQueryable提出CompletedCourses,我想用CompletedCourse.LicenseNumber选项的LicenseNumber属性填充FirstOrDefault()从我的许可证表中UserIdStateId匹配已完成的课程记录。

这是我的查询,但我认为这不会正确处理重复的许可证:

var entries =
    (from course in context.CompletedCourses
         join license in context.Licenses on course.UserId equals license.UserId
         where license.StateId == course.Course.StateId
         select course)
    .Include(x => x.Agent)
    .Include(x => x.Course.State);

这可以在一个查询中完成吗?提前谢谢。

2 个答案:

答案 0 :(得分:5)

以下是如何做到这一点:

var entries =
    (from course in context.CompletedCourses
     join license in context.Licenses
     on new { course.UserId, course.Course.StateId }
     equals new { license.UserId, license.StateId }
     into licenses
     let licenseNumber = licenses.Select(license => license.LicenseNumber).FirstOrDefault()
     select new { course, licenseNumber });

但请注意,对于此类投影,您的查询中不能包含Include(您可以,但它们不会生效)。

我从上面得到的EF生成的查询是:

SELECT 
    [Extent1].[CompletedCourseId] AS [CompletedCourseId], 
    [Extent1].[UserId] AS [UserId], 
    [Extent1].[LicenseNumber] AS [LicenseNumber], 
    [Extent1].[Course_CourseId] AS [Course_CourseId], 
    (SELECT TOP (1) 
        [Extent2].[LicenseNumber] AS [LicenseNumber]
        FROM  [dbo].[Licenses] AS [Extent2]
        INNER JOIN [dbo].[Courses] AS [Extent3] ON [Extent3].[StateId] = [Extent2].[StateId]
        WHERE ([Extent1].[Course_CourseId] = [Extent3].[CourseId]) AND ([Extent1].[UserId] = [Extent2].[UserId])) AS [C1]
    FROM [dbo].[CompletedCourses] AS [Extent1]

可以注意到EF有效地忽略了join,因此可以通过简单的自然查询获得相同的结果:

var entries =
    (from course in db.CompletedCourses
     let licenseNumber =
        (from license in db.Licenses
         where license.UserId == course.UserId && license.StateId == course.Course.StateId
         select license.LicenseNumber).FirstOrDefault()
     select new { course, licenseNumber });

答案 1 :(得分:0)

@ IvanStoev的回答非常有助于加入匿名类型,但最终我无法使用它,因为我需要包含。这是我使用的解决方案,它导致两个DB查询,而不是一个对我的情况很好的查询。

var entries = context.CompletedCourses
    .Include(x => x.Agent)
    .Include(x => x.Course);
var courses = entries.ToList();
var courseIds = entries.Select(x => x.CompletedCourseId);
var licenses =
    (from course in entries
        join license in context.Licenses
        on new { course.AgentId, course.Course.StateId } 
        equals new { AgentId = license.UserId, license.StateId }
        where courseIds.Contains(course.CompletedCourseId)
        select license);
foreach (var course in courses)
{
    var license = agentLicenses.FirstOrDefault(x => x.UserId == course.AgentId && 
        x.StateId == course.Course.StateId);
    if (license != null)
    {
        course.LicenseNumber = license.LicenseNumber;
    }
}
return courses;