.NET 8迁移后Fluent NHibernate的TimeSpan类型转换问题求助
问题重现
从.NET Framework 4迁移到.NET Core 8后,使用FluentNHibernate映射PostgreSQL的TIME字段到实体的TimeSpan属性时,出现以下错误:
System.FormatException: 输入字符串'07:30:00'格式不正确。
System.InvalidCastException: 无法将类型为'System.DateTime'的对象转换为类型'System.TimeSpan'。
修改实体属性为TimeSpan后,错误变为:
System.FormatException: 输入字符串'07:30:00'格式不正确。
System.InvalidCastException: 无法将类型为'System.TimeSpan'的对象转换为类型'System.IConvertible'。
已尝试.CustomType<TimeAsTimeSpanType>()和JSON自定义转换器,但返回IList<dynamic>的动态SQL查询仍报错。
环境信息
- 数据库:PostgreSQL
- 字段类型:TIME
- 实体代码:
public virtual TimeSpan column_time { get; set; }
- 现有映射代码:
Map(x => x.column_time).Column("column_time") .CustomType<TimeAsTimeSpanType>();
解决方案
1. 确保Npgsql与NHibernate版本兼容
.NET 8下需使用适配的Npgsql和NHibernate.Npgsql包,建议更新到最新稳定版:
- NuGet包:
NHibernate(>=5.4.0)、NHibernate.Npgsql(>=5.4.0)、Npgsql(>=8.0.0)
2. 使用Npgsql原生TimeSpan类型映射
替换原自定义类型为Npgsql提供的NpgsqlTimeSpanType,该类型专门处理PostgreSQL TIME与.NET TimeSpan的转换:
Map(x => x.column_time) .Column("column_time") .CustomType<NHibernate.Type.NpgsqlTimeSpanType>();
或者直接指定数据库类型:
Map(x => x.column_time) .Column("column_time") .CustomSqlType("TIME") .Type<TimeSpan>();
3. 修复动态SQL查询的类型转换
动态查询返回dynamic时,NHibernate无法自动处理类型映射,需显式转换字段类型:
- 在SQL中使用
CAST转换:
SELECT CAST(column_time AS TIME) AS column_time FROM your_table
- 或者在C#中手动转换动态结果:
var dynamicList = session.CreateSQLQuery("SELECT column_time FROM your_table").List<dynamic>(); var typedList = dynamicList.Select(item => { var timeString = item.column_time.ToString(); return TimeSpan.Parse(timeString); }).ToList();
4. 自定义NHibernate类型处理
如果上述方法无效,可实现自定义IType来处理转换逻辑:
public class PostgreSqlTimeSpanType : PrimitiveType { public PostgreSqlTimeSpanType() : base(new TimeSpanSqlType(DbType.Time)) { } public override string Name => "PostgreSqlTime"; public override Type ReturnedType => typeof(TimeSpan); public override object Get(IDataReader rs, int index) { var value = rs[index]; if (value is DBNull) return TimeSpan.Zero; if (value is DateTime dateTime) return dateTime.TimeOfDay; if (value is string timeStr) return TimeSpan.Parse(timeStr); return Convert.ToTimeSpan(value); } public override object Get(IDataReader rs, string name) { return Get(rs, rs.GetOrdinal(name)); } public override void Set(IDbCommand cmd, object value, int index) { var parameter = (IDbDataParameter)cmd.Parameters[index]; parameter.Value = value is TimeSpan ts ? ts.ToString() : DBNull.Value; } }
然后在映射中使用:
Map(x => x.column_time) .Column("column_time") .CustomType<PostgreSqlTimeSpanType>();
5. 检查Npgsql连接字符串配置
确保连接字符串中未禁用TimeSpan相关转换,添加或确认以下配置:
Host=your_host;Database=your_db;Username=user;Password=pass;ConvertTimeSpanToString=true
内容的提问来源于stack exchange,提问作者João Torres

