在 Linq-to-Entities 查询中格式化日期会导致异常

Posted

技术标签:

【中文标题】在 Linq-to-Entities 查询中格式化日期会导致异常【英文标题】:Formatting date in Linq-to-Entities query causes exception 【发布时间】:2011-08-15 21:54:47 【问题描述】:

我有提交日期时间的实体类,我想选择不同的 'mon-yyyy' 格式日期时间文件值并填充下拉列表。

下面的代码给了我错误:

var env = db.Envelopes.Select(d => new
        
            d.ReportDate.Year,
            d.ReportDate.Month,
            FormattedDate = d.ReportDate.ToString("yyyy-MMM")
        ).Select(d => d.FormattedDate)

    List<SelectListItem> _months = new List<SelectListItem>();         

    foreach (var mname in env)
    
        _months.Add(new SelectListItem()  Text = mname, Value = mname );
    

错误信息:

LINQ to Entities 无法识别 方法'System.String ToString(System.String)' 方法,以及 这个方法不能翻译成 商店表达式。

如何更正此错误消息?

谢谢 SR

【问题讨论】:

【参考方案1】:

请记住,您的查询将被转换为 SQL 并发送到数据库。查询不支持您尝试格式化日期,这就是您看到该特定错误消息的原因。您需要检索结果,然后在数据具体化后进行格式化。

一个选项是直接选择日期。在迭代结果时,在将其添加到列表时对其进行格式化。但是你也可以通过方法链在单个语句中实现格式化日期列表的构建。

List<SelectListItem> _months = db.Envelopes.OrderByDescending(d => d.ReportDate)
        .Select(d => d.ReportDate)
        .AsEnumerable() // <-- this is the key method
        .Select(date => date.ToString("MMM-yyyy"))
        .Distinct()
        .Select(formattedDate => new SelectListItem  Text = formattedDate, Value = formattedDate )
        .ToList(); 

.AsEnumerable() 方法将强制对数据库执行查询的第一部分,其余部分将处理内存中的结果。

【讨论】:

@Anthony Pegram:感谢您的回复。从您的代码中,我想按 ReportDate 的降序获取不同的值和顺序。我需要在你的代码中添加这两个。 我已经更新了答案,包括在AsEnumerable() 调用之前的降序日期排序和不同的日期。然后这些将成为 SQL 的一部分并由数据库处理。 @Anthony Pegram,抱歉我的问题不清楚。我想获得不同的 'yyyy-MMM' 值,而不是不同的报告日期。 @sfgroups,明白了。在这种情况下,您会将.Distinct() 移动到.AsEnumerable() 之后。我会再次更新。 2011-03-23 2011-04-19 2011-04-26 2011-05-26 我有这些价值。期待这个输出:May-2011 Apr-2011 Mar-2011【参考方案2】:

这里有一个替代方案:

.Select( p -> SqlFunctions.StringConvert((double)
                  SqlFunctions.DatePart("m", p.modified)).Trim() + "/" +
              // SqlFunctions.DateName("mm", p.modified) + "/" + MS ERROR?
              SqlFunctions.DateName("dd", p.modified) + "/" +
              SqlFunctions.DateName("yyyy", p.modified)

显然DateName("MM", ..) 拼出了月份名称,其中DatePart("mm", ..) 提供了一个数值,因此StringConvert( ),但是这个左边用空格填充了结果,因此.Trim()

就像 Anthony Pegram 上面所说的那样,这发生在数据库中而不是 C# 中(.AsEnumerable() 将所有本地数据提取到 C#,因此请确保在使用之前过滤数据。)

显然,您希望稍微重新排列输出以适合 yyyy-MM,并使用 DatePart 作为数字或使用 DateName 作为月份名称。

【讨论】:

喜欢这个答案,但是当人们使用类而不指示可以找到该类的命名空间时,它会杀死我。 SqlFunctions 位于 System.Data.Objects.SqlClient 命名空间中。您需要在项目中引用 System.Data.Entity 程序集才能找到它。【参考方案3】:

这是使用常见 dd/MM/yyyy 格式的替代方案。 使用 Entity Framework 5 在 SQL Server 2012 上确认

invoices.Select(i => new 

    FormattedDate = (     EntityFunctions.Right(String.Concat(" ", SqlFunctions.StringConvert((double?) SqlFunctions.DatePart("dd", i.DocumentDate))), 2)
                        + "/"
                        + EntityFunctions.Right(String.Concat(" ",SqlFunctions.StringConvert((double?) SqlFunctions.DatePart("mm", i.DocumentDate))), 2)
                        + "/"
                        + EntityFunctions.Right(SqlFunctions.StringConvert((double?) SqlFunctions.DatePart("yyyy", i.DocumentDate)), 4)
                       ).Replace(" ", "0")

以带前导零的 dd/MM/yyyy 格式生成日期。

【讨论】:

【参考方案4】:

我使用了一种解决方法来手动构建包含yyyyMM 的格式。如果它可以帮助某人(至少暂时),我想在这里提一下:

FormattedDate = d.ReportDate.Year.ToString() + "-" + d.ReportDate.Month.ToString()

这使得格式yyyy-MM

【讨论】:

以上是关于在 Linq-to-Entities 查询中格式化日期会导致异常的主要内容,如果未能解决你的问题,请参考以下文章

为啥 LINQ-to-Entities 将此查询放在子选择中?

Linq-to-Entities:带有 WHERE 子句和投影的 LEFT OUTER JOIN

使用 LINQ-to-Entities 搜索时忽略空字符串

如果存在-UPDATE-else-INSERT 与 Linq-to-Entities?

更新您的 edmx 以反映您的数据库中所做的更改(.net linq-to-entities)

在实体框架和 Linq to Entities 中使用规范模式和表达式