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

4
使用EF Core 5和SQL Server,我正在尝试弄清楚如何将作为字符串存储在LINQ查询中(因此在SQL Server端)的日期进行转换。
我已经在EF.Functions中搜索过了,但是我无法找到适用于这种日期解析的正确方法。
我还尝试过Convert.ToDateTime和DateTime.Parse,但它们似乎都没有LINQ翻译。
直接的SQL等价语句是Convert(datetime, mycolumn, 102)。 如果微软没有支持本地SQL中简单易用的东西,那么我会非常惊讶,所以如果我错过了什么明显的东西,我很抱歉,在放弃之前我在网上搜索并进行了几次尝试。
请注意,我知道一些解决方法,例如在应用Where()之前调用ToList(),或者修改DB(或创建视图)以更改列类型。
我的问题主要是:如何使用EF调用Convert(datetime, mycolumn, 102)。

5
为什么需要在 SQL 中进行转换?你的数据库中的列是什么数据类型,而你在 C# 中想要的数据类型又是什么? - gunr2171
4
“或者修改数据库(或创建视图)来解决问题。”我的意思是,如果你撞墙时很疼,不要把枕头放在墙上。这里是EF可以转换为MS Sql Server的列表。 - gunr2171
2
为什么不一开始就将日期存储在“日期”或“日期时间”列中呢? - Charlieface
3
在数据库中使用正确的数据类型存储日期不是“权宜之计”,而是正确的解决方法。你尝试做的是一种权宜之计。如果你正确地存储日期(甚至添加一个计算列来强制执行完整性并给你一个可以使用正确类型的列,比如db<>fiddle),那么你的问题就会消失,而且你就不用担心任何其他需要将日期作为实际日期而不是字符串处理的情况下的权宜之计了。 - GarethD
2
答案是“你不能这样做,因为微软不支持使用数据库管理系统不当的奇怪情况”。 - Ian Kemp
显示剩余7条评论
2个回答

14

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

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

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 功能的支持?我查看了一下,但没有找到任何相关的内容。谢谢。 - AFract
1
(1) 到目前为止还没有“开箱即用”的解决方案。您在链接中看到的就是它们支持的内容。一般来说,它们通过提供程序(数据库)特定方法扩展 EF.Functions 的系统还远非完美,如果您要针对多个数据库,则会面临诸多问题。集成的 CLR 映射更好,但是每个提供程序都可以选择支持翻译或不支持翻译,因此在运行时无法知道查询是否会被翻译。(2) linq2db 包对于 EF Core 缺少的某些功能(如(递归)CTE 和窗口函数)具有更好的支持,但可能不支持 db 特定函数。 - Ivan Stoev
你能帮我解答这个问题吗:https://stackoverflow.com/questions/68734040/problem-in-filtering-the-data-from-the-same-table - ILoveStackoverflow
您可以使用值转换器(https://learn.microsoft.com/en-us/ef/core/modeling/value-conversions)作为另一种选择。但是,这将使用SQL Server CAST将字符串转换为日期。因此,它使用登录的默认语言格式。如果您无法更改登录的默认语言格式,则必须运行SET LANGUAGE“British English”(或所需的语言),如果日期格式不是默认的美国MM / dd / yyyy格式。有关更多信息,请参见此处https://learn.microsoft.com/en-us/sql/t-sql/statements/set-language-transact-sql?view=sql-server-ver15 - Dasith Wijes

6

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

(DateTime)(object)t.Date 

查询内部完成任务。

例如:

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

会完美地工作。 我猜这背后有一点黑魔法,它只能适用于大多数“直接”的日期格式和文化转换。稍后我将使用 SQL 分析器或其他解决方案检查它发出的 SQL 类型。


这相当于 CAST(mycolumn AS DATE),它不允许您像 CONVERT 一样指定样式。 只要您的字符串可以转换为日期,这将按预期工作。 - GarethD
1
@GarethD 这就是我期望找到的。它能够工作,但显然我不会过于信任它,因为它严重依赖于文化等因素... - AFract
2
是的,我所说的“双重转换技巧”(你可以在我的其他帖子中找到它)是另一种(更通用的)方法,它可以转换为SqlServer 默认数据类型转换,这通常不是将数字/日期/时间数据存储为文本时使用的格式。 - Ivan Stoev
1
也可以处理以文本形式存储的整数,仅供参考。 - Ben

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接