You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 8迁移后Fluent NHibernate的TimeSpan类型转换问题求助

.NET 8中FluentNHibernate处理PostgreSQL TIME类型的解决方案

问题重现

从.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 16:20:10