You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET中转换数据库返回值格式并映射至ViewModel的问题

按需映射数据库结果到ViewModel的问题解决

需求描述

将包含ID、Username、HistorDay、Clock字段的Model,映射到包含ID、Username、HistorDay、check-in、check-out字段的ViewModel。需按Username和HistorDay分组,把Clock列表中**奇数索引(index%2!=0)**的值存入check-in,**偶数索引(index%2==0)**的值存入check-out。

第一次尝试及问题

代码

var Filter = _context.Attendances
             .GroupBy(x => new { x.Username, x.HistorDay }) 
             .Select(x => new {
           Username = x.Key.Username, 
           HistorDay = x.Key.HistorDay,
           In = x.Select(c => c.Clock).Where((q, w) => w % 2 != 0),
           Out = x.Select(c => c.Clock).Where((q, w) => w % 2 == 0),
}); 

异常

System.Collections.Generic.IEnumerable1[Eams.Models.Attendance]`

原因

EF Core无法将带索引参数的Where((q,w)=>...)表达式转换为SQL语句,直接在IQueryable上执行这类操作会抛出转换异常。

第二次尝试及输出

代码

var rawData = _context.Attandances
              .Select(a => new { a.Username, a.HistorDay, a.Clock })
              .ToList();

var result = rawData
            .GroupBy(x => new { x.Username, x.HistorDay })
            .Select(x => new 
            {
                Username = x.Key.Username,
                HistorDay = x.Key.HistorDay,
                In = x.Select(c => c.Clock).Where((q, w) => w % 2 != 0).ToList(),
                Out = x.Select(c => c.Clock).Where((q, w) => w % 2 == 0).ToList(),
            });

foreach (var item in result)
{
    Debug.WriteLine($"ALL {item}");
}

控制台输出

ALL { Username = 43091, HistorDay = 6/23/2023 12:00:00 AM, In = System.Collections.Generic.List1[System.TimeSpan], Out = System.Collections.Generic.List1[System.TimeSpan] }

说明

这段代码逻辑本身是正确的:先调用ToList()将数据加载到内存,再用LINQ to Objects处理索引筛选,避免了EF Core的SQL转换问题。但控制台输出显示集合类型而非具体值,是因为直接打印匿名对象时,集合只会输出类型名称,需要遍历集合才能看到具体元素。

优化方案(映射到ViewModel)

首先定义ViewModel:

public class AttendanceViewModel
{
    public int ID { get; set; }
    public string Username { get; set; }
    public DateTime HistorDay { get; set; }
    public List<TimeSpan> CheckIn { get; set; }
    public List<TimeSpan> CheckOut { get; set; }
}

修改代码完成映射并输出具体值:

var rawData = _context.Attendances
              .Select(a => new { a.ID, a.Username, a.HistorDay, a.Clock })
              .ToList();

var viewModelList = rawData
            .GroupBy(x => new { x.Username, x.HistorDay })
            .Select(g => new AttendanceViewModel
            {
                ID = g.First().ID, // 取分组内第一条数据的ID,可根据业务规则调整
                Username = g.Key.Username,
                HistorDay = g.Key.HistorDay,
                CheckIn = g.Select((c, idx) => (c.Clock, idx))
                           .Where(t => t.idx % 2 != 0)
                           .Select(t => t.Clock)
                           .ToList(),
                CheckOut = g.Select((c, idx) => (c.Clock, idx))
                            .Where(t => t.idx % 2 == 0)
                            .Select(t => t.Clock)
                            .ToList()
            })
            .ToList();

// 打印具体数据示例
foreach (var vm in viewModelList)
{
    Debug.WriteLine($"用户名: {vm.Username}, 日期: {vm.HistorDay:yyyy-MM-dd}");
    Debug.WriteLine("签到记录:");
    foreach (var inTime in vm.CheckIn)
    {
        Debug.WriteLine($"  {inTime}");
    }
    Debug.WriteLine("签退记录:");
    foreach (var outTime in vm.CheckOut)
    {
        Debug.WriteLine($"  {outTime}");
    }
}

关键要点

  1. 必须先通过ToList()将数据库数据加载到内存,再使用LINQ to Objects处理带索引的筛选逻辑,避免EF Core的SQL转换错误。
  2. 映射ViewModel时,ID字段需根据业务规则处理(示例中取分组第一条数据的ID)。
  3. 打印集合数据时需遍历元素,才能输出具体值而非集合类型名称。

内容的提问来源于stack exchange,提问作者OmarNology

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 06:15:06