EF Core数据库优先模式下SQL Server date列映射DateOnly报错排查
问题:SQL Server date列映射EF Core DateOnly? 抛出InvalidCastException
我有一个SQL Server表,其中某列定义为:
date NULL
对应的实体类将该列映射为DateOnly?类型的属性,应用中未定义任何自定义转换器,但尝试映射时抛出以下错误:
InvalidCastException: Unable to cast object of type 'System.DateTime' to type 'System.DateOnly'
即使在DbContext中重写ConfigureConventions()并添加以下代码,错误仍然存在:
configBuilder.Properties<DateOnly?>().HaveColumnType("date");
已知EF Core 8.0开始原生支持DateOnly,但在数据库优先模式下连接现有数据库时出现此问题,本地空白数据库测试无异常。
代码示例
MyEntity.cs
[Table("ExistingTableName")] [Keyless] public class MyEntity { [Column("ExistingColumnName")] public required DateOnly? Date {get; set;} }
AppDbContext.cs
public class AppDbContext : DbContext { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) {} public DbSet<MyEntity> MyTable {get; set;} }
调用代码
public async Task GetEntity(AppDbContext db) { var data = await db.MyTable.Select(row => row.Date).FirstAsync(); // 错误在此抛出 }
补充信息
- 在单独项目和本地空白数据库中测试未出现错误,连接现有数据库则报错,检查列属性与本地库几乎无差异
- 删除项目中未在查询中引用的其他实体类和
DbSet后问题依旧 - 新建项目使用原连接字符串连接现有数据库,仍然抛出
InvalidCastException - 数据库版本为15.0.4365.2(SQL Server 2019 CU22)
可能的解决方法
1. 显式添加值转换器
针对现有数据库,显式指定DateOnly的转换器,避免EF Core错误地将date列读取为DateTime:
在DbContext的ConfigureConventions()中添加:
protected override void ConfigureConventions(ModelConfigurationBuilder configBuilder) { base.ConfigureConventions(configBuilder); configBuilder.Properties<DateOnly?>() .HaveConversion<DateOnlyConverter, DateOnlyComparer>(); }
或者直接在实体属性上标注转换器:
[Column("ExistingColumnName", TypeName = "date")] [ValueConverter(typeof(DateOnlyConverter))] public required DateOnly? Date { get; set; }
2. 检查并升级依赖版本
确保项目使用EF Core 8.0及以上版本,同时升级Microsoft.Data.SqlClient到最新稳定版,旧版本驱动可能对DateOnly的支持存在兼容性问题。
3. 确认数据库列的实际类型
执行以下SQL语句,确认列的真实类型是否为date:
SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'ExistingTableName' AND COLUMN_NAME = 'ExistingColumnName';
若返回结果不是date,需修正列类型。
4. 清除或禁用查询缓存
查询编译缓存可能保留了旧的类型映射逻辑,尝试在查询中添加AsNoTracking():
var data = await db.MyTable.AsNoTracking().Select(row => row.Date).FirstAsync();
或者临时禁用全局查询缓存(仅用于测试):
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { QueryTrackingBehavior = QueryTrackingBehavior.NoTrackingWithIdentityResolution; }
5. 使用原生SQL强制转换
如果以上方法无效,尝试用原生SQL查询并强制转换类型:
var data = await db.MyTable .FromSql($"SELECT CAST(ExistingColumnName AS date) AS ExistingColumnName FROM ExistingTableName") .Select(row => row.Date) .FirstAsync();
内容的提问来源于stack exchange,提问作者RotundChinchilla
相关产品推荐
相关产品推荐

