postgresql 更新时间,null 值被当成字符串string,不能识别报错 返回
any520 发布于2025/9/2
实体类时间属性
[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"


热忱回答(6)
-
fate sta VIP0
2025/9/2C#代码怎么写的。 关键的代码
0 回复 -
fate sta VIP0
2025/9/2
0 回复 -
fate sta VIP0
2025/9/2提供一下代码
0 回复 -
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 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 回复 -
周明秀 VIP0
1周前我们在项目中也遇到一样的问题,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 回复