postgresql 更新时间,null 值被当成字符串string,不能识别报错 返回

SqlSugar 沟通中
6 538
该叫什么 any520 发布于2025/9/2
悬赏:0 飞吻

实体类时间属性

[SugarColumn(IsNullable = true)]

public DateTimeOffset? EndTime { get; set; }


使用的 postgresql, 在给 EndTime 赋值为 null 后。

报错 42804: column "end_time" is of type timestamp with time zone but expression is of type text



// 后来找到 https://www.donet5.com/home/doc?masterId=1&typeId=1221 自定义修改列方法

// 通过将 pars 中 @Const1 类型为 AnsiString 的强行修改为 DateTimeOffset,能成功执行。

// 但是这个感觉很奇怪, pars 中能看到有个 @end_time 类型是 DateTimeOffset 的,却不去执行。

scopeProvider.Aop.OnExecutingChangeSql = (sql, pars) =>

{

foreach (var p in pars)

{

if ((p.Value == null || p.Value == DBNull.Value))

{

if (p.DbType == System.Data.DbType.AnsiString)

{

// 告诉 PG 这是 timestamptz 的 NULL

// p.DbType = System.Data.DbType.DateTimeOffset; // 或 DbType.DateTime

}

}

}

return new KeyValuePair<string, SugarParameter[]>(sql, pars);

};


sql 的值是:


UPDATE "sys_tasks"  SET

"end_time" = @Const1   WHERE ( "id" = @Id2 )


pars 的值

pars[1]:

DbType = AnsiString

ParameterName = "@Const1"

pars[3]:

DbType = DateTimeOffset

ParameterName = "@end_time"



image.pngimage.png

热忱回答6

  • C#代码怎么写的。 关键的代码

    0 回复
  • image.png

    0 回复
  • 提供一下代码

    0 回复
  • any520 any520 VIP0
    2025/9/2

    实体类

    public class SysTasks

    {

    [Display(Name = "Scheduler.TaskId")]

    [SugarColumn(IsPrimaryKey = true, IsIdentity = false)]

    public long Id { get; set; }

    [Display(Name = "Scheduler.EndTime")]

    [SugarColumn(IsNullable = true)]

    public DateTimeOffset? EndTime { get; set; }


    }

    // 调用

    public Task<int> UpdateTasksAsync(SysTasks parm)

    {

    return UpdateAsync(f => f.Id == parm.Id, f => new SysTasks

    {

    Id = parm.Id,

    EndTime = parm.EndTime,

    });

    }


    public Task<int> UpdateAsync(Expression<Func<T, bool>> where, Expression<Func<T, T>> columns,

    CancellationToken ct = default)

    { // SimpleClient

    return Context.Updateable<T>()

    .SetColumns(columns)

    .Where(where)

    .RemoveDataCache()

    .ExecuteCommandAsync(ct);

    }


    0 回复
  • any520 any520 VIP0
    2025/9/2


    换成这种实体直接放到 Updateable 的就可以,为啥呢


    return UpdateAsync(new SysTasks

    {

    Id = parm.Id,

    EndTime = parm.EndTime,

    });



    public
    Task<int> UpdateAsync(T entity, bool ignoreNullColumns = false, object? data = null,

    CancellationToken ct = default)

    {

    return Context.Updateable(entity)

    .IgnoreColumns(ignoreNullColumns)

    .EnableDiffLogEventIF(data != null, data)

    .ExecuteCommandAsync(ct);

    }


    0 回复
  • 我们在项目中也遇到一样的问题,PG/海量数据库下:IDataReader 把某些可空基础类型确实有误读为 string 后,IDataReaderEntityBuilder 内部的 CheckType会报错的情况。下面是我的解决方法:
    思路:新建一个可空类型属性转换器,对Nullable类型统一挂载该转换器,绕过 IDataReaderEntityBuilder 内部的 CheckType 抛错路径

    /// <summary>
    /// 解决 PG/海量数据库下:IDataReader 把某些可空基础类型误读为 string 后,
    /// IDataReaderEntityBuilder 的强制类型校验导致转换失败的问题。
    /// 通过对 Nullable 统一挂载该转换器,绕过 IDataReaderEntityBuilder 内部的 CheckType 抛错路径。
    /// </summary>
    public class NullablePropertyConvert : ISugarDataConverter
    {
        public SugarParameter ParameterConverter<T>(object columnValue, int columnIndex)
        {
            var name = "@NullableScalarConv" + columnIndex;
            var type = typeof(T);
            // Fastest/BulkMerge 会把 ParameterConverter 的返回值写进 DataRow;DataRow 要求 SQL 空值为 DBNull,不能用 C# null。
            return columnValue == null || columnValue == DBNull.Value ? new SugarParameter(name, DBNull.Value, Nullable.GetUnderlyingType(type) ?? type) : new SugarParameter(name, columnValue, UtilMethods.GetUnderType(type));
        }
    
        public T QueryConverter<T>(IDataRecord dr, int dataRecordIndex)
        {
            var value = dr.GetValue(dataRecordIndex);
            if (value == DBNull.Value)
            {
                return default;
            }
            //value是string转可空类型,做针对常见类型进行特殊解析,其他情况使用SqlSugar官方的 UtilMethods.ChangeType2
            var target = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
            switch (value)
            {
                case string s when string.IsNullOrWhiteSpace(s):
                    return default;
                // 针对常见类型做更宽松的解析,避免 Convert.ChangeType 因格式差异失败。
                case string s when target == typeof(int):
                    return (T)(object)int.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
                case string s when target == typeof(long):
                    return (T)(object)long.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
                case string s when target == typeof(short):
                    return (T)(object)short.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
                case string s when target == typeof(decimal):
                    return (T)(object)decimal.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
                case string s when target == typeof(double):
                    return (T)(object)double.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
                case string s when target == typeof(float):
                    return (T)(object)float.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
                case string s when target == typeof(bool):
                    return s switch
                    {
                        "1" => (T)(object)true,
                        "0" => (T)(object)false,
                        _ => (T)(object)bool.Parse(s)
                    };
                case string s when target == typeof(Guid):
                    if (Guid.TryParse(s, out var g)) return (T)(object)g;
                    return default;
                case string s when target == typeof(DateTime):
                    var dt = DateTime.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);
                    return (T)(object)dt;
                case string s when target == typeof(DateTimeOffset):
                    var dto = DateTimeOffset.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);
                    return (T)(object)dto;
                default:
                    return (T)UtilMethods.ChangeType2(value, typeof(T));
            }
        }
    }
    
    创建一个新的扩展配置,设置可空类型列使用该转换器:
    public class CustomConfigureExternalServices : ConfigureExternalServices{    public CustomConfigureExternalServices()
        {
            EntityService = ColumnMapper;
        }    public void ColumnMapper(PropertyInfo property, EntityColumnInfo column)
        {        var propertyType = property.PropertyType;        var underType = Nullable.GetUnderlyingType(propertyType);        if (column.SqlParameterDbType == null
                && propertyType.IsGenericType
                && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>) && underType != null && IsSupportedScalarUnderType(underType))
            {            //PG数据库的Nullable类型,使用NullablePropertyConvert转换器
                column.SqlParameterDbType = typeof(NullablePropertyConvert);
            }
        }    private static bool IsSupportedScalarUnderType(Type underType)
        {        return underType == typeof(int) || underType == typeof(long) || underType == typeof(short) || underType == typeof(decimal) || underType == typeof(double) || underType == typeof(float) || underType == typeof(bool) || underType == typeof(DateTime) || underType == typeof(DateTimeOffset) || underType == typeof(Guid);
        }
    }
    
    最后再链接配置中使用扩展配置:
         connectionConfig.ConfigureExternalServices = new CustomConfigureExternalServices
         {
         };



    0 回复