简体   繁体   English

如何使用 linq 在 2 个以上的表上使用左连接和非左连接

[英]How do I use a left-join and non-left-join on more than 2 tables using linq

I am trying to join 3 tables into a specific model using linq but I am getting incorrect results.我正在尝试使用 linq 将 3 个表加入特定的 model 但我得到的结果不正确。

With my code below:使用下面的代码:

var filteredEmployees = _employeesRepository.GetAll(); //TODO: Filter

var filteredSchedules = _schedulesRepository.GetAll(); //TODO: Filter

var filteredPunches = _punchesRepository.GetAll(); //TODO: Filter

var innerGroupJoinQuery =
        from allEmployees in filteredEmployees
        join allSchedule in filteredSchedules on allEmployees.FileNumber equals allSchedule.FileNumber
        join allPunches in filteredPunches on allSchedule.Id equals allPunches.ScheduleId
        select new {Employee = allEmployees, Schedule = allSchedule, Punches = allPunches}; <--  this part doesnt seem correct to me

var innerGroupJoinQueryList = innerGroupJoinQuery.ToList();

var inner2 = innerGroupJoinQueryList
        .GroupBy(g => g.Employee)
        .Select(s => new JanusWeeklyOverviewDto()
        {
                Employee = ObjectMapper.Map<EmployeesDto>(s.Key),
                ScheduleAndPunches = s.Select(s2 => new ScheduleAndPunchesDto()
                {
                        Schedule = ObjectMapper.Map<SchedulesDto>(s2.Schedule),
                        Punches = s.Where(w => w.Schedule.Id == w.Punches.ScheduleId).Select(s3 => ObjectMapper.Map<PunchesDto>(s3.Punches)),
                })
        })
        .ToList();

Problem 1 :问题1

In the ScheduleAndPunches model, there is exactly 7 Schedule record for every Employee between a given week range (Sunday 2/14 to Saturday 2/20) but it currently only returns records that have a Punch entry.ScheduleAndPunches model 中,在给定的一周范围(2/14 星期日到 2/20 星期六)之间,每个Employee恰好有 7 Schedule记录,但它目前只返回具有Punch条目的记录。

Q1 : How would I change my code so that it does a left-join and pulls all 7 Schedule records and the Punch record associated (if any). Q1 :我将如何更改我的代码,以便它进行左连接并提取所有 7 Schedule记录和关联的Punch记录(如果有)。

Problem 2 :问题2

In the ScheduleAndPunches model, punches returns all Punches record, ignoring the Schedule.Id its associated with.ScheduleAndPunches model 中,punches 返回所有Punches记录,忽略与其关联的Schedule.Id

Q2 : How would I change my code so that it pulls all Punches records based on the Schedule.Id . Q2 :我将如何更改我的代码,以便它根据Schedule.Id提取所有Punches记录。

The SQL equivalent query would look like this: SQL 等效查询如下所示:

在此处输入图像描述

SELECT TOP (100) PERCENT
    Janus.Employees.FileNumber,
    Janus.Employees.HomeDepartment,
    Janus.Employees.LastName,
    Janus.Employees.FirstName,
    Janus.Schedules.Id AS ScheduleId,
    Janus.Schedules.ScheduleDate,
    Janus.Schedules.EndDate,
    Janus.Schedules.StartTime,
    Janus.Schedules.EndTime,
    Janus.Schedules.ForcedLabor,
    Janus.Punches.Id   AS PunchId,
    Janus.Punches.TimeStamp,
    Janus.Punches.OriginalTimeStamp,
    Janus.Punches.CurrentState,
    Janus.Punches.WasOverridden,
    Janus.Punches.OverrideId,
    Janus.Punches.EnteredBy,
    Janus.Punches.Comments
FROM
    Janus.Employees
        INNER JOIN      Janus.Schedules
                        ON Janus.Employees.FileNumber = Janus.Schedules.FileNumber
        LEFT OUTER JOIN Janus.Punches
                        ON Janus.Schedules.Id = Janus.Punches.ScheduleId -- AND Janus.Schedule.FileNumber = Janus.Punches.FileNumber
WHERE
      (Janus.Employees.FileNumber = '8095')
  AND (Janus.Schedules.ScheduleDate BETWEEN '20210214' AND '20210220')
ORDER BY
    Janus.Employees.FileNumber,
    Janus.Schedules.ScheduleDate,
    Janus.Punches.TimeStamp DESC

The expected model to return looks like this:预期的 model 返回如下所示:

public class JanusWeeklyOverviewDto
{
    public EmployeesDto Employee { get; set; }
    public IEnumerable<ScheduleAndPunchesDto> ScheduleAndPunches { get; set; }
}

public class ScheduleAndPunchesDto
{
    public SchedulesDto Schedule { get; set; }
    public IEnumerable<PunchesDto> Punches { get; set; }
}

public class EmployeesDto  : EntityDto
{
    public string FileNumber { get; set; }

    public string LastName { get; set; }

    public string FirstName { get; set; }

    public string BadgeNumber { get; set; }

    public DateTime? HiredDate { get; set; }

    public DateTime? DateOfBirth { get; set; }

    public decimal? Rate { get; set; }

    public string RateType { get; set; }

    public string HomeDepartment { get; set; }

    public string Status { get; set; }

    public int? LunchRule { get; set; }

    public DateTime? EffectiveDate { get; set; }

    public string Comments { get; set; }

    public string CurrentState { get; set; }

    public string OvertimeState { get; set; }

    public DateTime? TerminationDate { get; set; }

    public string Role { get; set; }

    public string Profile { get; set; }

    public byte[] Photo { get; set; }

    public int? PhotoFileId { get; set; }
}

public class SchedulesDto : EntityDto
{
    public string FileNumber { get; set; }

    public DateTime? ScheduleDate { get; set; }

    public DateTime? StartTime { get; set; }

    public DateTime? EndTime { get; set; }

    public DateTime? EndDate { get; set; }

    public string ForcedLabor { get; set; }
}

public class PunchesDto : EntityDto
{
    public string FileNumber { get; set; }

    public DateTime TimeStamp { get; set; }
    public DateTime OriginalTimeStamp { get; set; }

    public string CurrentState { get; set; }

    public string WasOverridden { get; set; }

    public string OverrideId { get; set; }

    public string EnteredBy { get; set; }

    public string Comments { get; set; }

    public byte[] EmployeePhoto { get; set; }

    public int? EmployeePhotoFileId { get; set; }

    public byte[] ManagerPhoto { get; set; }

    public int? ManagerPhotoFileId { get; set; }

    public int? ScheduleId { get; set; }
    
    public string AppVersion { get; set; }
    
    public bool DeviceWasOffline { get; set; }
}

I've tried using the method syntax because i'm not to great at the query syntax:我尝试使用方法语法,因为我不擅长查询语法:

var query = filteredEmployees
    .GroupJoin(filteredSchedules, e => e.FileNumber, s => s.FileNumber, (e, s) => new { Employee = e, Schedule = s})
    .SelectMany(o => o.Schedule.DefaultIfEmpty(), (l, r) => new { l.Employee, Schedule = r })

    .GroupJoin(filteredPunches, t => t.Schedule.Id, p => p.ScheduleId, (t, p) => new { t.Employee, t.Schedule, Punch = p })
    .SelectMany(o => o.Punch.DefaultIfEmpty(), (l, r) => new { l.Employee, l.Schedule, Punch = r })
    
    .Select(x => new {
        FileNumber = x.Employee.FileNumber,
        Name = x.Employee.FirstName,
        ScheduleId = x.Schedule.Id,
        StartTime = x.Schedule.StartTime,
        EndTime = x.Schedule.EndDate,
        ActualPunchTime = x.Punch?.TimeStamp
    })
    .ToList();

I've cut down the required amount of fileds for testing, but as far as i can tell it's what your after:我已经减少了测试所需的文件数量,但据我所知,这就是你所追求的:

 ToString()       : { FileNumber = 12345, Name = Lee, ScheduleId = 1, StartTime = 02/19/2021 00:00:00, EndTime = 02/19/2021 09:00:00, ActualPunchTime =  }
 ToString()       : { FileNumber = 12345, Name = Lee, ScheduleId = 2, StartTime = 02/18/2021 00:00:00, EndTime = 02/19/2021 00:00:00, ActualPunchTime = 02/19/2021 21:08:48 }
 ToString()       : { FileNumber = 12345, Name = Lee, ScheduleId = 3, StartTime = 02/17/2021 00:00:00, EndTime = 02/18/2021 00:00:00, ActualPunchTime =  }
 ToString()       : { FileNumber = 12345, Name = Lee, ScheduleId = 4, StartTime = 02/17/2021 00:00:00, EndTime = 02/18/2021 00:00:00, ActualPunchTime =  }
 ToString()       : { FileNumber = 12345, Name = Lee, ScheduleId = 5, StartTime = 02/17/2021 00:00:00, EndTime = 02/18/2021 00:00:00, ActualPunchTime =  }
 ToString()       : { FileNumber = 12346, Name = Bob, ScheduleId = 6, StartTime = 02/17/2021 00:00:00, EndTime = 02/18/2021 00:00:00, ActualPunchTime = 02/19/2021 21:08:48 }

My Test data:我的测试数据:

    var filteredEmployees = new List<Employee> { 
    new Employee 
    {
        Id = 1,
        FileNumber = "12345",
        FirstName = "Lee",
    },
    new Employee 
    {
        Id = 2,
        FileNumber = "12346",
        FirstName = "Bob",
    } 
};

var filteredSchedules = new List<Schedule> 
{
    new Schedule {
        Id = 1,
        FileNumber = "12345",
        StartTime = DateTime.Now.Date,
        EndDate = DateTime.Now.Date.AddHours(9)
    },
    new Schedule {
        Id = 2,
        FileNumber = "12345",
        StartTime = DateTime.Now.AddDays(-1).Date,
        EndDate = DateTime.Now.AddDays(-1).AddHours(9).Date
    },
    new Schedule {
        Id = 3,
        FileNumber = "12345",
        StartTime = DateTime.Now.AddDays(-2).Date,
        EndDate = DateTime.Now.AddDays(-2).AddHours(9).Date
    },
    new Schedule {
        Id = 4,
        FileNumber = "12345",
        StartTime = DateTime.Now.AddDays(-2).Date,
        EndDate = DateTime.Now.AddDays(-2).AddHours(9).Date
    },
    new Schedule {
        Id = 5,
        FileNumber = "12345",
        StartTime = DateTime.Now.AddDays(-2).Date,
        EndDate = DateTime.Now.AddDays(-2).AddHours(9).Date
    },
    new Schedule {
        Id = 6,
        FileNumber = "12346",
        StartTime = DateTime.Now.AddDays(-2).Date,
        EndDate = DateTime.Now.AddDays(-2).AddHours(9).Date
    },
};
var filteredPunches = new List<Punches> 
{
    new Punches {
        Id = 1,
        ScheduleId = 2,
        TimeStamp = DateTime.Now
    },
    new Punches {
        Id = 2,
        ScheduleId = 6,
        TimeStamp = DateTime.Now
    }
};

You can view my fiddle here: https://dotnetfiddle.net/jpWRh8你可以在这里查看我的小提琴: https://dotnetfiddle.net/jpWRh8

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM