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

13
我有一个带有日期时间字段的实体类,我想选择不同的'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

4个回答

18

请记住,您的查询将被翻译成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按降序排序。我需要在你的代码哪里添加这两个东西? - sfgroups
我已经更新了答案,包括按降序日期排序和在 AsEnumerable() 调用之前进行去重。这些将成为 SQL 的一部分,并由数据库处理。 - Anthony Pegram
@Anthony Pegram,抱歉我的问题可能不太清楚。我想获取不同的“yyyy-MMM”值,而不是不同的reportdate。 - sfgroups
2011年3月23日 2011年4月19日 2011年4月26日 2011年5月26日。我有这些值,期望输出为:May-2011 Apr-2011 Mar-2011 - sfgroups
@sfgroups,翻转格式字符串,我只是在问题的代码中使用了您提供的格式字符串。对于您想要的格式,您需要使用“MMM-yyyy”。 - Anthony Pegram
显示剩余2条评论

10

这里有一个备选方案:

.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 来获取数字或者月份名称。


4
喜欢这个答案,但是当人们使用类而不指明类所在的命名空间时,让我感到很不爽。SqlFunctions在System.Data.Objects.SqlClient命名空间中。你需要在项目中引用System.Data.Entity程序集才能找到它。 - Tim

2

这里有一种使用常见的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。


1
我使用了一个解决方法来手动构建包含yyyyMM的格式。如果可能的话,我想在这里提及它,以帮助某些人(至少是暂时的):
FormattedDate = d.ReportDate.Year.ToString() + "-" + d.ReportDate.Month.ToString()

这使得格式为yyyy-MM

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