【问题标题】:How to convert string to DateTime in C# EF Core query如何在 C# EF Core 查询中将字符串转换为 DateTime
【发布时间】:2020-07-13 02:14:06
【问题描述】:
var res = Context.Exampletable
                 .Where(s => s.CompanyId == CompanyId &&  
                             Convert.ToDateTime(s.TextDate) >= DateTime.Now)
                 .Select(x => new Exampletable { TextDate = x.TextDate })
                 .FirstOrDefault();

这是我的问题陈述之一的 Linq。我想从当前日期和时间戳中获取记录未来的日期记录,因此我将其转换并与Datetime 进行比较,但出现此错误:

LINQ 表达式 'DbSet
.Where(a => Convert.ToDateTime(a.TextDate) > Convert.ToDateTime(DateTime.Now))' > 无法翻译。以可翻译的形式重写查询,或通过插入对 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync() 的调用显式切换到客户端评估

注意:在 Postgresql DB 中,TextDate 列具有字符串数据类型并包含诸如“4/1/2020 10:00 AM”之类的值。

请为此提供解决方案。

【问题讨论】:

  • 它不起作用,因为 Postgresql 的 LINQ to Entities 实现不知道如何将 Convert.ToDateTime(string) 转换为 SQL

标签: entity-framework linq entity-framework-core postgresql-9.1 ef-core-3.0


【解决方案1】:

如果您真的无法更改基础列类型,那么不要使用不受支持的Convert.ToDateTime,而是使用映射到 PostgreSQL CAST 运算符的 C# cast 运算符:

(DateTime)(object)s.TextDate >= DateTime.Now

请注意,“中间”转换为 object 只是为了让 C# 编译器满意。


附:我真的不知道为什么支持Convert 的某些方法,例如ToInt32,而其他类似ToDateTime 的方法不支持。我猜这只是另一个 EF Core 不一致。

【讨论】:

  • Ivan Stoev 谢谢你的建议,我试过了,LinQ 现在正在翻译成 SQL,但它只获取匹配的记录,如 TextDate== DateTime.Now 和 >= ,
  • 这将是令人惊讶的,因为 cast 将字符串转换为时间戳数据类型,所以比较应该使用时间戳规则。确保您正确使用它,例如上面转换为CAST(t."TextDate" AS timestamp without time zone) < NOW(),“未提供正确输出”的 SQL 部分看起来如何?
  • 这里列出了支持的函数的完整列表docs.microsoft.com/en-us/ef/core/providers/sql-server/functions您可以使用EF CORE值转换器实现上述docs.microsoft.com/en-us/ef/core/modeling/value-conversions虽然值转换器支持CAST但不支持CONVERT的原因是一个有趣的.我认为这是因为 CONVERT 可能会在不同的数据库提供程序中给出不一致的结果。如果需要使用 CONVERT SQL Server 功能,可以试试这个方法stackoverflow.com/a/68729840/422427
  • @DasithWijes 问题是关于 PostgreSQL (Npgsql provider),因此 Sql Server 函数映射无关紧要。值转换器呢,这些适用于这种情况,因为它们会尝试将 DateTime 参数转换为字符串,而不是将字符串列转换为 DateTime。
  • @IvanStoev 值转换器会将字符串转换为日期时间,反之亦然。如果您不确定,请查看它们。生成的 SQL 将使用 CAST 操作将字符串转换为日期时间,然后执行 >= 操作。您建议的解决方案有一个额外的缺点,即它不适用于 InMemory DB,而值转换器方法在这两种情况下都能正常工作。
【解决方案2】:

人们决定将 DateTime 值存储为字符串,然后命令数据库的用户使用这些值进行计算,这总是让我感到困惑。我可以想象你想诅咒决定这样做的人,特别是因为他决定以这种不可排序的方式存储它。

最佳解决方案

如果可能,请更改数据库以将 DateTimes 存储为 DateTimes,或者如果您的数据库语言不知道如何执行此操作,请将 DateTimes 的 Ticks 存储为 longs。该数据库的未来用户将荣耀您的名字!

long nowTicks = DateTime.Now.Ticks;
var result = Context.Exampletable
    .Where(example => example.CompanyId == CompanyId && example.DateTicks >= nowTicks);

几乎是最好的解决方案

如果将 DateTimes 保存为字符串的决定是 PostgreSQL 开发人员的决定,那么尝试查找他们是否有处理这些日期时间的函数,尤其是比较

“情况越来越糟”的解决方案

尝试找出 PostgreSQL 是否具有字符串操作功能,以便您可以将 4/1/2020 10:00 AM 翻译成 IComparable。如果您想编写代码来将此值与例如 2019 年 4 月 1 日上午 10:00 或 2020 年 4 月 2 日上午 10:00 进行比较是很困难的,所以我想这将是一项艰巨的工作在 SQL 中

【讨论】:

  • Harald Coppoolse :感谢这个终极解决方案,我创建了 Datetimetick 列并按照建议的方式存储了 datetime 的 tikcs 值,它运行良好。我能够获取准确的记录。非常感谢:)
【解决方案3】:

在 EF Core 5+ 中,您可以使用 ValueConverters 作为此方案的“解决方法”,并使用内置的 StringDateTimeConverter (https://apisof.net/catalog/e0dd77d4-73c3-6bba-e51a-4842a59894d1)。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<Exampletable>()
        .Property(e => e.TextDate)
        .HasConversion<string>();
}

public class ExampleTable
{
 public DateTime TextDate {get; set;}
}

那么简单

DateTimeOffset someValue = some-value-here;
var query = DbContext.Entities.Where(e => e.TextDate <= someValue);

重要的是,您的 LINQ 查询中的参数是 datetimeoffset ,因为如果您不这样做,EF CORE 不会为该列生成带有 CAST 操作的 SQL。有关示例,请参见此处https://gist.github.com/dasiths/19b885c58442226d9fc8b89bc78511e4

生成的 SQL 会是这样的

((@__startSearch_0 >= CAST([s].[TextDate]) AS datetimeoffset))

编辑:重申一下,在这里使用值转换器是一种“解决方法”。要查看对选项的完整分析,我在https://dasith.me/2022/01/23/ef-core-datetime-conversion-rabbit-hole/ 处使用值转换器 hack 编写了对“变通办法”的完整详细分析。

【讨论】:

  • 不行,生成的SQL不同,没有强制转换,重试。
  • @IvanStoev 我不知道您使用的是哪个 EF Core 版本,但我使用的版本肯定会生成。
  • 我只能确认@Ivan 的发现,不知道你的发现会有什么不同。似乎我们不知道某些使 EF 切换的情况。请注意,考虑到可搜索性,不转换数据库值的查询比反向更有意义,所以我猜它更有可能是 EF 的标准实现。
  • @IvanStoev 我昨晚用样品设置。你能运行它,看看它产生了什么。 gist.github.com/dasiths/19b885c58442226d9fc8b89bc78511e4 我想我知道有什么区别。我在测试时在我的 LINQ 查询中使用了 datetimeoffset 参数 (SomeValue) 而不是 datetime。 EF Core 在比较之前使用 CAST 将列值转换为日期时间偏移量。我不知道这是 EF Core 的预期行为还是错误。我将更新我的答案以表明这一点。
  • @DasithWijes 那是另一个故事。是的,它会产生你所说的。然而,这不是因为值转换(您可以删除 HasConversion&lt;string&gt;() 并在 SQL 中获得几乎相同的转换),而是因为 C# 在 LINQ 查询表达式树中发出从 DateTimeOffsetDateTime 的隐藏隐式转换,然后被翻译为CASTs。因此,回顾一下,价值转换并不是解决这个问题的方法。实际上没有解决方案 - 我的 workaround 仅适用于某些提供商。当然,最好的办法是人们将他们的日期存储为日期而不是字符串。
猜你喜欢
  • 2021-10-14
  • 2014-03-01
  • 2022-01-08
  • 2021-03-31
  • 1970-01-01
  • 2014-03-30
  • 1970-01-01
  • 2012-05-05
  • 1970-01-01
相关资源
最近更新 更多