将含FORMAT命令的SQL查询转换为可在Entity Framework Core中使用的实现方案
解决方案
你之前调用.FromSqlRaw()报错的原因是直接在绑定了完整表结构的DbSet<ScrapLog>上执行查询,EF要求返回结果必须和ScrapLog实体的字段完全匹配,你返回自定义的两列自然无法适配,可通过以下两种方案实现需求:
方案1:沿用原生SQL执行
如果不想修改原有SQL逻辑,只要新增一个接收结果的DTO类,再配置无键实体即可:
- 定义结果接收类
public class ScrapCountDto { public string Date { get; set; } public int Quantity { get; set; } }
- 在你的DbContext中注册该类为无键实体
在OnModelCreating方法中添加配置:
modelBuilder.Entity<ScrapCountDto>().HasNoKey();
- 在DbContext中添加对应DbSet属性
public DbSet<ScrapCountDto> ScrapCounts { get; set; }
- 执行查询即可
var result = await _context.ScrapCounts .FromSqlRaw(@"SELECT FORMAT(datetime_scrapped, 'MMMM-yy') as Date, count(*) as Quantity FROM scrap_log GROUP BY FORMAT(datetime_scrapped, 'MMMM-yy')") .ToListAsync();
方案2:使用原生EF Core LINQ实现(推荐)
不需要写硬编码的SQL,完全由EF自动生成查询语句,性能更好也更易维护,还能避免SQL注入风险:
var result = await _context.ScrapLogs // 先按年、月数字分组,比直接按格式化字符串分组效率更高 .GroupBy(s => new { s.DateTimeScrapped.Year, s.DateTimeScrapped.Month }) .Select(g => new ScrapCountDto { // 内存中拼接成需要的年月格式,避免数据库侧的格式化性能损耗 Date = new DateTime(g.Key.Year, g.Key.Month, 1).ToString("MMMM-yy"), Quantity = g.Count() }) // 可选:按时间倒序排列 .OrderByDescending(x => x.Date) .ToListAsync();
如果需要固定返回英文月份全称,只要在ToString时指定区域信息即可:
Date = new DateTime(g.Key.Year, g.Key.Month, 1).ToString("MMMM-yy", CultureInfo.GetCultureInfo("en-US"))
EF Core原生支持上述写法,会自动转换为符合数据库语法的SQL语句,不需要手动处理FORMAT函数的映射逻辑。
注意事项
如果你需要适配除SQL Server之外的其他数据库,优先使用方案2的LINQ写法,不需要调整SQL语句,EF会自动处理不同数据库的时间函数差异。
内容的提问来源于stack exchange,提问作者Bennyono
相关产品推荐
相关产品推荐

