使用 LINQ 创建特定数据集

Creating a specific dataset with LINQ

我无法从通过 Dapper 从数据库中获取的对象列表创建一个特定的“数据集”。所以事情是关于公司的日常出勤,所以用户来了,点击,出去,点击,我们有一个 table 行,如下所示: sql table 所以我有一个像这样的模型 class:

    public class Attendance
    {
        public int UserId { get; set; }
        public string FullName { get; set; }
        public DateTime DateTimeUtcIn { get; set; }
        public DateTime DateTimeUtcOut { get; set; }
        public string DayOfTheWeek { get; set; }
        public int LocationId { get; set; }
        public TimeSpan TotalTime { get; set; }
        public byte StatusId { get; set; }
        public TimeSpan BreakTime { get; set; }
    }

然后在调用数据库后得到这样的结果 IEnumerable<Attendace>:

var result = await _platformDB.Con.QueryAsync<Models.Attendance.Attendance>(query);

然后我创建了一个像这样的简单视图模型:

    public class Time
    {
        public DateTime DateTimeUtcIn { get; set; }
        public DateTime DateTimeUtcOut { get; set; }
    }

    public class AttendaceViewModel
    {
        public int UserId { get; set; }
        public string FullName { get; set; }
        public string Date { get; set; }
        public string DayOfTheWeek { get; set; }
        public List<Time> TimesInAndOut { get; set; }
        public string Locations { get; set; }
        public string TotalTime { get; set; } //need to calculate
        public string Status { get; set; }
        public string BreakTime { get; set; } //need to calculate
     }

现在问题来了:我如何根据按 UserId 分组的结果创建 List<AttendaceViewModel> 并计算一天的 TotalTime(所有 dateTimeUtcIn + dateTimeUtcOut 的总和)和这一天的休息时间并填充此 List<Time> TimesInAndOut 在这里? 我真的不知道。

仅使用 LINQ 可能无法完成此操作。您应该能够遍历刚刚获取的结果中返回的项目,并在那里执行您的逻辑。

像这样实现一个函数,并在检索结果后调用它。如果您愿意,您甚至可以在此函数中处理数据库查询,然后只需调用该函数并取回您的列表。

private List<AttendanceViewModel> GetAttendanceViewModels((whatever type 'results' is goes here) {type} results)
{
    var output = new List<AttendanceViewModel>();
    foreach(result in results)
    {
        var totalTime = CalculateTotalTimeForOneDay(result);
        var breakTime = CalculateBreakTime(result);
        result.TotalTime = totalTime;
        result.BreakTime = breakTime;
    }
    return output.GroupBy(u => u.UserId)
}

我想我知道你需要什么:

List<AttendaceViewModel> resultList = result
    .GroupBy(a => (a.UserId, a.FullName, DateIn: a.DateTimeUtcIn.Date, a.DayOfTheWeek, a.LocationId, a.StatusId))
    .Select(ag => new AttendaceViewModel
    {
        UserId = ag.Key.UserId,
        FullName = ag.Key.FullName,
        Date = ag.Key.DateIn.ToString(),
        DayOfTheWeek = ag.Key.DayOfTheWeek,
        Locations = ag.Key.LocationId.ToString(),
        Status = ag.Key.StatusId.ToString(),
        TotalTime = new TimeSpan(ag.Sum(a => (a.DateTimeUtcOut - a.DateTimeUtcIn).Ticks)).ToString(),
        TimesInAndOut = ag.Select(a => new Time { DateTimeUtcIn = a.DateTimeUtcIn, DateTimeUtcOut = a.DateTimeUtcOut }).ToList(),
        BreakTime = CalcBreaks(ag.Select(a => (a.DateTimeUtcIn, a.DateTimeUtcOut))).ToString()
    })
    .ToList();

对于 BreakTime 我使用了这个辅助方法:

private static TimeSpan CalcBreaks(IEnumerable<(DateTime inTime, DateTime outTime)> times)
{
    if (!times.Skip(1).Any())
        return TimeSpan.Zero;

    TimeSpan totalBreakTime = TimeSpan.Zero;
    TimeSpan lastLeaveTime = times.First().outTime.TimeOfDay;

    foreach(var attendanceTime in times.OrderBy(at => at.inTime).ThenBy(at => at.outTime).Skip(1))
    {
        TimeSpan breakTime = attendanceTime.inTime.TimeOfDay - lastLeaveTime;
        totalBreakTime += breakTime;
        lastLeaveTime = attendanceTime.outTime.TimeOfDay;
    }

    return totalBreakTime;
}