.NET 6中EF Core使用DateOnly.AddDays()报类型转换错误的解决办法
.NET 6中解决DateOnly.AddDays()的类型转换错误
问题背景
使用Npgsql EF Core在.NET 6中对DateOnly字段调用AddDays()进行查询时,会抛出以下错误:
System.InvalidCastException: Can't cast database type timestamp without time zone to DateOnly
这是因为.NET 6版本的Npgsql EF Core在处理DateOnly的日期运算时,生成的SQL会返回timestamp without time zone类型,而该类型无法自动映射回DateOnly。该问题已在.NET 8版本的Npgsql EF Core中修复。
解决办法
1. 显式转换结果类型
在查询中手动将运算结果转换为DateOnly,通过DateOnly.FromDateTime()包裹,确保类型匹配:
var query = x.TestEntities .Select(x => new { x.Id, NextMonth = DateOnly.FromDateTime( x.Date.ToDateTime(TimeOnly.MinValue).AddDays(x.Diff) ) });
2. 注册自定义数据库函数
通过注册自定义函数,让EF Core生成正确的SQL,强制数据库返回date类型:
首先创建静态工具类:
public static class DateOnlyFunctions { public static DateOnly AddDays(DateOnly date, int days) => date.AddDays(days); }
在DbContext的OnModelCreating方法中注册函数的SQL转换逻辑:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.HasDbFunction(() => DateOnlyFunctions.AddDays(default, default)) .HasTranslation(args => new SqlFunctionExpression( "CAST", new[] { new SqlBinaryExpression( ExpressionType.Add, args[0], new SqlFunctionExpression("make_interval", new[] { new SqlConstantExpression(args[1]) }, typeof(TimeSpan)), typeof(DateTime) ), new SqlConstantExpression("DATE") }, typeof(DateOnly), null)); }
查询时使用自定义函数替代原生AddDays():
var query = x.TestEntities .Select(x => new { x.Id, NextMonth = DateOnlyFunctions.AddDays(x.Date, x.Diff) });
3. 使用原生SQL查询
直接编写原生SQL,确保返回的日期字段为date类型:
var results = await x.TestEntities .FromSqlInterpolated($@" SELECT ""Id"", ""Date"" + INTERVAL '{x.Diff} days' AS ""NextMonth"" FROM ""TestEntities""") .AsEnumerable() .Select(row => new { row.Id, NextMonth = DateOnly.FromDateTime(row.Date) }) .ToArrayAsync();
4. 升级到.NET 7(可选)
如果项目允许,升级到.NET 7可获得更完善的DateOnly支持,该问题在.NET 7的Npgsql EF Core版本中已得到部分修复,能覆盖大部分常规场景。
内容的提问来源于stack exchange,提问作者Lan Lin
相关产品推荐
相关产品推荐

