使用 Entity Framework Core 在 LINQ 查询中将字符串转换为 DateTime

Posted

技术标签:

【中文标题】使用 Entity Framework Core 在 LINQ 查询中将字符串转换为 DateTime【英文标题】:Convert String to DateTime in LINQ query with Entity Framework Core 【发布时间】:2021-10-14 02:49:08 【问题描述】:

使用 EF Core 5 和 SQL Server,我试图弄清楚如何在 LINQ 查询中转换存储为字符串的日期(在 SQL Server 端)。

我在EF.Functions 中进行了搜索,但我找不到此类日期解析的正确方法。

我也尝试过Convert.ToDateTimeDateTime.Parse,但它们似乎都没有 LINQ 翻译。

直接 SQL 等效项是 Convert(datetime, mycolumn, 102)。 如果微软没有做任何事情来支持原生 SQL 中简单易用的东西,我会感到非常惊讶,所以如果我错过了一些明显的东西,我很抱歉,但我已经在网上搜索并做了一些尝试,然后才投降。

PS:请注意,我知道在应用 Where() 之前调用 ToList() 或修改数据库(或创建视图)以更改列类型等变通方法。

我的问题主要是:如何用EF调用Convert(datetime, mycolumn, 102)

【问题讨论】:

为什么需要in sql进行转换?你的db中的列是什么数据类型,c#中你想要的数据类型是什么? "或修改数据库(或创建视图)以解决问题。"我的意思是,如果你的头撞到墙上很痛,不要放枕头。 Here 是 EF 可以转换为 MS Sql Server 的列表。 为什么日期不首先存储在datedatetime 列中? 使用正确的数据类型在数据库中存储日期不是“解决方法”,而是正确的解决方案。您正在尝试做的是解决方法。如果您正确存储日期(或者甚至添加一个计算列以强制完整性并为您提供一个与正确类型一起使用的列,例如db<>fiddle),那么您的问题就会消失,您无需担心这里的解决方法, 或任何其他您需要将日期视为实际日期而不是字符串的时间 答案是“你不能,因为微软不支持你错误地使用 DBMS 的奇怪场景”。 【参考方案1】:

您可以使用 EF Core 标量函数映射相对轻松地添加所需的不受支持的方法。

例如,添加以下类(带有必要的用法):

namespace Microsoft.EntityFrameworkCore

    public static class SqlFunctions
    
        public static DateTime? ToDateTime(this string s, int format) => throw new NotSupportedException();

        public static ModelBuilder AddSqlFunctions(this ModelBuilder modelBuilder)
        
            modelBuilder.HasDbFunction(() => ToDateTime(default, default))
                .HasTranslation(args => new SqlFunctionExpression(
                    functionName: "CONVERT",
                    arguments: args.Prepend(new SqlFragmentExpression("date")),
                    nullable: true,
                    argumentsPropagateNullability: new[]  false, true, false ),
                    type: typeof(DateTime),
                    typeMapping: null));

            return modelBuilder;
        
    


第二种方法是为了方便,做实际的映射。

现在你只需要从OnModelCreating覆盖调用它:

if (Database.IsSqlServer()) modelBuilder.AddSqlFunctions();

然后在 LINQ to Entities 查询中使用它:

var query = db.Set<MyEntity>()
    .Where(e => e.MyProp.ToDateTime(102) > DateTime.Today
    .ToQueryString();
// SELECT ..... WHERE Convert(date, [e].[MyProp], 102) > CONVERT(date, GETDATE())

【讨论】:

感谢自定义 sql 函数的示例。如果每个人都确认没有内置功能,我会使用它。尽管缺乏本机解决方案,但您最终是否知道一个 nuget 包(可能专用于 sql server),它添加了这种东西来改进 EFCore 中的 SQL Server 功能支持?我看了看,但没有发现任何相关的东西。谢谢。 (1) 目前还没有“开箱即用”的解决方案。您在链接中看到的是他们支持的内容。一般来说,如果您针对多个数据库,他们使用提供者(数据库)特定方法扩展EF.Functions 的系统远非完美。集成的 CLR 映射更好,但再次允许每个提供程序支持翻译,因此在运行时之前无法知道查询是否会翻译 (2) linq2db 包对 EF Core 中缺少的某些内容有更好的支持像(递归)CTE 和窗口函数,但可能不适用于 db 特定函数。 你能帮我解决这个问题吗:***.com/questions/68734040/… 您可以使用值转换器 (docs.microsoft.com/en-us/ef/core/modeling/value-conversions) 作为另一个选项。不过,这使用 SQL Server CAST 将字符串转换为日期。所以它使用登录默认语言格式。如果您无法更改登录默认语言格式,那么如果日期格式不是默认的 US MM/dd/yyyy 格式,则必须运行 SET LANGUAGE "British English"(或您想要的语言)。请参阅此处了解更多信息docs.microsoft.com/en-us/sql/t-sql/statements/…【参考方案2】:

谈到奇怪的解决方案,我注意到使用

(DateTime)(object)t.Date 

在查询内部完成这项工作。

例如:

dbContext.Table.Max(t => (DateTime)(object)t.Date);

将完美运行。 我想这背后有一点黑魔法,它只适用于大多数“直接”日期格式和文化转换。稍后我将使用 SQL 分析器或其他解决方案检查它发出什么样的 SQL。

【讨论】:

这相当于CAST(mycolumn AS DATE),它不允许您像CONVERT 那样指定样式。只要您的字符串将CAST 到一个日期,尽管这将按预期工作 @GarethD 这是我期望找到的。它有效,但显然我不会太相信它,因为它严重依赖于文化,所以...... 是的,我所说的“双重转换技巧”(你可以在我的其他帖子中找到它)是另一种(更通用的)方法,它可以转换为 SqlServer default 数据类型转换,通常可能不是将数字/日期/时间数据存储为文本时使用的格式。 也适用于存储为文本的整数,仅供参考

以上是关于使用 Entity Framework Core 在 LINQ 查询中将字符串转换为 DateTime的主要内容,如果未能解决你的问题,请参考以下文章

Entity Framework Core 性能优化

在 Entity Framework Core 中使用 [ComplexType]

在 Entity Framework Core 中使用 SQL 视图

使用 Entity Framework Core 更新相关数据

使用 Entity Framework Core 自动增加部分主键

如何使用 Entity Framework Core 模拟异步存储库