Search code examples
c#entity-frameworklinqlinq-query-syntax

Linq query with first or default join


I have the following data model:

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

I'm trying to come up with an IQueryable for CompletedCourses and I would like to populate CompletedCourse.LicenseNumber with the LicenseNumber property of the FirstOrDefault() selection from my Licenses table where UserId and StateId match the completed course records.

Here is my query, but I don't think this will handle duplicate licenses correctly:

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

Is this something that can be done in a single query? Thanks in advance.


Solution

  • @IvanStoev's answer was very helpful in joining on anonymous types, but ultimately I couldn't use it because I needed Includes. Here is the solution I went with that results in two DB queries instead of one which is fine for my situation.

    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;