Npgsql EF 6:使用 ExecuteSqlInterpolatedAsync 时时间戳失败
Npgsql EF 6: Timestamp fails when using ExecuteSqlInterpolatedAsync
更新到 6.0.3 后,我在 ExecuteSqlInterpolatedAsync 中遇到错误。
好吧,我知道这是一个重大变化,但我查看了数据库中的所有表,所有列都是“没有时区的时间戳”。通过 model/DbSet 进行更新或插入是可以的。
考虑到这一点,看看这段代码:
await Database.ExecuteSqlInterpolatedAsync($@"update nfservico set
chavenfse = {chave},
lotenfse = {cLote},
lotenfse_dh = {dhRecbto},
lotenfse_sit = {sit},
lotenfse_mot = {mot}
where id = {id}");
dhRecbto 这是一个没有指定“种类”的 DateTime 参数。 运行 给出以下错误:
System.InvalidCastException: Cannot write DateTime with Kind=Unspecified to PostgreSQL type 'timestamp with time zone', only UTC is supported. Note that it's not possible to mix DateTimes with different Kinds in an array/range. See the Npgsql.EnableLegacyTimestampBehavior AppContext switch to enable legacy behavior.
at Npgsql.Internal.TypeHandlers.DateTimeHandlers.TimestampTzHandler.ValidateAndGetLength(DateTime value, NpgsqlParameter parameter)
at Npgsql.Internal.TypeHandlers.DateTimeHandlers.TimestampTzHandler.ValidateObjectAndGetLength(Object value, NpgsqlLengthCache& lengthCache, NpgsqlParameter parameter)
at Npgsql.NpgsqlParameter.ValidateAndGetLength()
at Npgsql.NpgsqlParameterCollection.ValidateAndBind(ConnectorTypeMapper typeMapper)
at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken can...
我无法理解“带时区的时间戳”部分,正如我之前所说,该字段没有时区。
我不得不做一个丑陋的工作:
var sql = new StringBuilder();
sql.AppendLine("update nfservico set ");
sql.AppendLine($" chavenfse = '{chave}',");
sql.AppendLine($" lotenfse = '{cLote}',");
sql.AppendLine($" lotenfse_dh = '{dhRecbto:yyyy-MM-dd HH:mm:ss}',");
sql.AppendLine($" lotenfse_sit = {sit},");
sql.AppendLine($" lotenfse_mot = '{mot}'");
sql.AppendLine($" where id = {id}");
await Database.ExecuteSqlRawAsync(sql.ToString());
有人遇到同样的问题吗?不,我不想使用“EnableLegacyTimestampBehavior”开关,想要正确地使用它(至少就像在 Npgsql 6 的发行说明中找到的那样)。
这是 EF Core 原始 SQL 支持的限制。 EF 类型映射 - 管理发送参数的 PG 类型(时间戳与时间戳) - 仅由参数的 CLR 类型 (DateTime) 确定,而不查看其内容(即种类)。而DateTime默认的EF映射是timestamptz,不是timestamp;因此错误。请注意,这与您的实际数据库列类型无关:这纯粹是一个 client-side 问题。
幸运的是,EF 的原始 SQL API 允许直接传入 DbParameter,允许您准确指定所需的 PostgreSQL 类型:
var p = new NpgsqlParameter { Value = new DateTime(2020, 1, 1, 12, 0, 0), NpgsqlDbType = NpgsqlDbType.Timestamp };
_ = ctx.Database.ExecuteSqlInterpolatedAsync($"SELECT {p}");
这是传递参数的安全、正确的方法,其中 CLR 类型 (DateTime) 具有默认的 PG 类型 (timestamptz),这不是您想要的(您需要时间戳)。您上面的解决方法可能在某些方面是错误的,并且可能会引入不需要的时区转换。
更新到 6.0.3 后,我在 ExecuteSqlInterpolatedAsync 中遇到错误。 好吧,我知道这是一个重大变化,但我查看了数据库中的所有表,所有列都是“没有时区的时间戳”。通过 model/DbSet 进行更新或插入是可以的。
考虑到这一点,看看这段代码:
await Database.ExecuteSqlInterpolatedAsync($@"update nfservico set
chavenfse = {chave},
lotenfse = {cLote},
lotenfse_dh = {dhRecbto},
lotenfse_sit = {sit},
lotenfse_mot = {mot}
where id = {id}");
dhRecbto 这是一个没有指定“种类”的 DateTime 参数。 运行 给出以下错误:
System.InvalidCastException: Cannot write DateTime with Kind=Unspecified to PostgreSQL type 'timestamp with time zone', only UTC is supported. Note that it's not possible to mix DateTimes with different Kinds in an array/range. See the Npgsql.EnableLegacyTimestampBehavior AppContext switch to enable legacy behavior.
at Npgsql.Internal.TypeHandlers.DateTimeHandlers.TimestampTzHandler.ValidateAndGetLength(DateTime value, NpgsqlParameter parameter)
at Npgsql.Internal.TypeHandlers.DateTimeHandlers.TimestampTzHandler.ValidateObjectAndGetLength(Object value, NpgsqlLengthCache& lengthCache, NpgsqlParameter parameter)
at Npgsql.NpgsqlParameter.ValidateAndGetLength()
at Npgsql.NpgsqlParameterCollection.ValidateAndBind(ConnectorTypeMapper typeMapper)
at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken can...
我无法理解“带时区的时间戳”部分,正如我之前所说,该字段没有时区。
我不得不做一个丑陋的工作:
var sql = new StringBuilder();
sql.AppendLine("update nfservico set ");
sql.AppendLine($" chavenfse = '{chave}',");
sql.AppendLine($" lotenfse = '{cLote}',");
sql.AppendLine($" lotenfse_dh = '{dhRecbto:yyyy-MM-dd HH:mm:ss}',");
sql.AppendLine($" lotenfse_sit = {sit},");
sql.AppendLine($" lotenfse_mot = '{mot}'");
sql.AppendLine($" where id = {id}");
await Database.ExecuteSqlRawAsync(sql.ToString());
有人遇到同样的问题吗?不,我不想使用“EnableLegacyTimestampBehavior”开关,想要正确地使用它(至少就像在 Npgsql 6 的发行说明中找到的那样)。
这是 EF Core 原始 SQL 支持的限制。 EF 类型映射 - 管理发送参数的 PG 类型(时间戳与时间戳) - 仅由参数的 CLR 类型 (DateTime) 确定,而不查看其内容(即种类)。而DateTime默认的EF映射是timestamptz,不是timestamp;因此错误。请注意,这与您的实际数据库列类型无关:这纯粹是一个 client-side 问题。
幸运的是,EF 的原始 SQL API 允许直接传入 DbParameter,允许您准确指定所需的 PostgreSQL 类型:
var p = new NpgsqlParameter { Value = new DateTime(2020, 1, 1, 12, 0, 0), NpgsqlDbType = NpgsqlDbType.Timestamp };
_ = ctx.Database.ExecuteSqlInterpolatedAsync($"SELECT {p}");
这是传递参数的安全、正确的方法,其中 CLR 类型 (DateTime) 具有默认的 PG 类型 (timestamptz),这不是您想要的(您需要时间戳)。您上面的解决方法可能在某些方面是错误的,并且可能会引入不需要的时区转换。