如何在IQueryable查询中实现TimeSpan类型的剧集总时长求和
解决方案:无需转IEnumerable直接计算剧集总时长
当然有办法!你遇到的问题是因为EF无法直接翻译TimeSpan.Ticks的访问和后续的TimeSpan构造操作。下面提供两种适配不同EF版本的方案:
方案1:EF Core 5.0+ 直接求和TimeSpan
如果你的项目使用的是EF Core 5.0或更高版本,可以直接对TimeSpan类型的属性调用Sum,EF Core会自动将这个操作翻译成数据库能理解的SQL(比如SQL Server中会用DATEADD累加时间):
IQueryable<Course> result = db.Courses; GetCoursesViewModel list = new GetCoursesViewModel(); list.Courses = await result .Skip(skip) .Take(getCourses.Take) .Select(c => new GetCoursesListViewModel { Id = c.CourseId, Title = c.CourseTitle, Price = c.CoursePrice, Group = c.CourseGroup.GroupTitle, Level = c.CourseLevel.LevelTitle, Status = c.CourseStatus.StatusTitle, ImageName = c.CourseImageName, EpisodeCount = c.CourseEpisodes.Count, ShortDescription = c.CourseShortDescription, TotalTime = c.CourseEpisodes.Sum(e => e.EpisodeTime), // 直接求和TimeSpan,EF Core 5+支持 Teacher = c.User.UserName }).ToListAsync();
方案2:兼容旧版EF/数据库的秒数转换法
如果你用的是EF Core 5以下版本,或者数据库不支持直接累加时间类型,可以先将TimeSpan转换为总秒数(或毫秒、Ticks)求和,再在查询中构造TimeSpan。这里要注意,必须让求和操作在数据库端执行,所以要使用EF能翻译的表达式:
IQueryable<Course> result = db.Courses; GetCoursesViewModel list = new GetCoursesViewModel(); list.Courses = await result .Skip(skip) .Take(getCourses.Take) .Select(c => new GetCoursesListViewModel { Id = c.CourseId, Title = c.CourseTitle, Price = c.CoursePrice, Group = c.CourseGroup.GroupTitle, Level = c.CourseLevel.LevelTitle, Status = c.CourseStatus.StatusTitle, ImageName = c.CourseImageName, EpisodeCount = c.CourseEpisodes.Count, ShortDescription = c.CourseShortDescription, // 先求和总秒数,再转换为TimeSpan TotalTime = TimeSpan.FromSeconds(c.CourseEpisodes.Sum(e => EF.Functions.DateDiffSecond(TimeSpan.Zero, e.EpisodeTime))), Teacher = c.User.UserName }).ToListAsync();
这里用EF.Functions.DateDiffSecond来获取每个EpisodeTime的总秒数,这个函数能被EF翻译成对应的SQL,确保求和操作在数据库端完成,不需要提前转成IEnumerable。
为什么你的原代码不行?
原代码中e.EpisodeTime.Ticks的访问是EF无法翻译的——因为数据库中并没有直接对应Ticks的字段,EF不知道如何将这个属性访问转换为SQL语句。所以我们需要用EF能识别的方式来表达时间求和的逻辑。
内容的提问来源于stack exchange,提问作者Hadi
相关产品推荐
相关产品推荐

