为什么LINQ to Entities无法识别方法'System.String ToString()'?

31

在MVC3 Web应用程序中出现错误。

LINQ to Entities不认识方法'System.String ToString()'方法,且这种方法无法被转换为存储表达式。

当我尝试使用EF从查询中获取值时:

public class DataRepository
    {
        public mydataEntities1 dbContext = new mydataEntities1();

        public List<SelectListItem> GetPricingSecurityID()
        {
        var pricingSecurityID = (from m in dbContext.Reporting_DailyNAV_Pricing
                                     select new SelectListItem
                                         {
                                                Text = m.PricingSecurityID.ToString(),
                                                Value = m.PricingSecurityID.ToString()
                                         });

        return pricingSecurityID.ToList();
        }
    }
8个回答

55

那无法转换为 SQL。我猜理论上可能可以,但并未实现。

你只需要在获得结果后执行投影操作:

var pricingSecurityID = (from m in dbContext.Reporting_DailyNAV_Pricing
                                     select m.PricingSecurityID).AsEnumerable()
    .Select(x => new SelectListItem{ Text = x.ToString(), Value = x.ToString() });

6
这里不需要调用 ToList() - 因为使用 AsEnumerable 就可以在减少负载的情况下完成工作,为什么要构建另一个列表呢? - Jon Skeet
2
@JonSkeet 这取决于您是否需要立即操作结果,或者您想等到编译器实际执行查询。 - Mandeep Janjua
7
@MandeepJanjua:无论如何,OP最终都会调用ToList来返回一个列表 - 调用ToList().Select(...).ToList()没有任何意义。 - Jon Skeet

18

如果它已经是一个字符串,为什么在第一次调用 ToString 时还要费心?我怀疑在 LINQ to Entities 中没有包含翻译是因为这是无意义的。将您的选择子句更改为:

select new SelectListItem
{
    Text = m.PricingSecurityID,
    Value = m.PricingSecurityID
}

如果你真的需要做一些LINQ to Entities不支持的操作,可以使用AsEnumerable将查询从数据库转移到进程内部:

public List<SelectListItem> GetPricingSecurityID()
{
    return dbContext.Reporting_DailyNAV_Pricing
                    .Select(m => m.PricingSecurityID)
                    .AsEnumerable() // Rest of query is local
                    // Add calls to ToString() if you really need them...
                    .Select(id => new SelectListItem { Text = id, Value = id })
                    .ToList();
}

顺便说一下,我也同意Jason的反对意见。你最好返回一个List<string>,然后在其他地方进行渲染。

此外,请注意,如果你只是使用单个select子句或者只是一个where子句,查询表达式确实没有太多优势 - 调用LINQ扩展方法可能会少些混乱,特别是如果你想调用不支持查询表达式的方法(例如ToList)。


你是否将 System.String ToString() 解读为 PricingSecurityID 已经是一个 string?我认为它实际上是指 ToString 的返回类型,而 PricingSecurityID 可能(很可能?)不是一个 string - jason
@Jason:是的,那是我的猜测。也许我错了,如果是这样,我的第二段代码(带有ToString调用)将是正确的方法。我认为你的方法只是拆分投影并不能帮助 - 它仍然会在数据库中执行,除非你将查询实现或使用AsEnumerable。 - Jon Skeet
我刚刚测试了一下:如果源数据类型不是字符串,错误消息也会提到 System.String ToString() - Heinzi

4

因为它试图将其转换为SQL,但无法转换。在调用ToString之前,请进行投影操作,然后再返回给调用者。因此,请使用以下内容替换您的select子句:

select m.PricingSecurityID

然后说:
return pricingSecurityID
           .AsEnumerable()
           .Select(x => x.ToString())
           .Select(x => new SelectListItem { Text = x, Value = x })
           .ToList();

此外,我注意到您正在混合UI问题和数据查询问题。这通常是一个不好的做法。实际上,您应该只返回ID列表,让代码的UI部分担心将其改造成正确的形式。

4

这个怎么样?在这个例子中,数据库中的VDN字段和Skill字段都是整数。我需要从两个字段中寻找匹配项,因此需要进行两次比较。

包括这个:

using System.Data.Objects.SqlClient; // needed to convert numbers to strings for linq

在进行数字比较时,请执行以下操作:

        // Search Code
            if (!String.IsNullOrEmpty(searchString))
            {
                depts = depts.Where(d => SqlFunctions.StringConvert((double)d.VDN).Contains(searchString.ToUpper())
                || SqlFunctions.StringConvert((double)d.Skill).Contains(searchString.ToUpper()));
            }
        // End Search Code

工作。


3
你介意修改一下你的回答,让它更贴近问题吗?目前它们之间的联系不是很明显。 - Kevin A. Naudé

3

遗憾的是,EF不知道如何转换.ToString()。您必须使用嵌入函数SqlFunctions.StringConvert:http://msdn.microsoft.com/en-us/library/dd466292.aspx 另外,int没有重载,所以您必须进行类型转换为double:-(

var vendors = 
   from v in Vendors  
   select new
   {             
       Code = SqlFunctions.StringConvert((double)v.VendorId)
   }; 

2
我知道这个问题已经有答案了,我同意使用AsEnumerable()。然而,我想强调一个常见的场景,我通常会遇到使用AsEnumerable()来解决此错误的低效方法。
来自.NET语言集成查询关系数据

与ToList()和ToArray()不同,AsEnumerable()运算符不会导致查询执行。它仍然是延迟的。AsEnumerable()运算符仅改变查询的静态类型,将IQueryable转换为IEnumerable,欺骗编译器将其余的查询视为本地执行。

参考文献
  1. 我是否误解了LINQ to SQL .AsEnumerable()?
  2. 理解LINQ to SQL中的.AsEnumerable()
低效的方式
IEnumerable<InvoiceDTO> inefficientEnumerable = 
     (from a in db.Invoices
     where a.Practice_Key == practiceKey.FirstOrDefault()
     select a
     ).AsEnumerable().
     Select(x => new InvoiceDTO
                             {
                                 InvoiceID = x.InvoiceID,
                                 PracticeShortName = x.Dim_Practice.Short_Name,
                                 InvoiceDate = x.InvoiceDate,
                                 InvoiceTotal = x.InvoiceAmount,
                                 IsApproved = x.IsApproved,
                                 InvoiceStatus = (
                                                  x.IsApproved == null ? "Pending" :
                                                  x.IsApproved == true ? "Approved" :
                                                  x.IsApproved == false ? "Rejected" : "Unknown"
                                                ),
                                 InvoicePeriodStartDateText = x.InvoicePeriodStart.ToShortDateString(),
                                 InvoicePeriodEndDateText = x.InvoicePeriodEnd.ToShortDateString(),
                                 InvoicePeriodStartDate = x.InvoicePeriodStart,
                                 InvoicePeriodEndDate = x.InvoicePeriodEnd
                             }
                             );

            invoices = inefficientEnumerable.ToList();

在这里,AsEnumerable被用于整个表格。即使不需要,所有列都被选择了。

更好的方式

 IQueryable<InvoiceDTO> invoicesQuery = 
   (from a in db.Invoices
   where a.Practice_Key == practiceKey.FirstOrDefault()
   select new InvoiceDTO
            {
             InvoiceID = a.InvoiceID,
             PracticeShortName = a.Dim_Practice.Short_Name,
             InvoiceDate = a.InvoiceDate,
             InvoiceTotal = a.InvoiceAmount,
             IsApproved = a.IsApproved,
             InvoiceStatus = (
                               a.IsApproved == null ? "Pending" :
                               a.IsApproved == true ? "Approved" :
                               a.IsApproved == false ? "Rejected" :"Unknown"
                               ),
             InvoicePeriodStartDate = a.InvoicePeriodStart,
             InvoicePeriodEndDate = a.InvoicePeriodEnd
          });


          IEnumerable<InvoiceDTO> betterEnumerable = invoicesQuery.AsEnumerable().
          Select(x => new InvoiceDTO
                                 {
                                     InvoiceID = x.InvoiceID,
                                     PracticeShortName = x.PracticeShortName,
                                     InvoiceDate = x.InvoiceDate,
                                     InvoiceTotal = x.InvoiceTotal,
                                     IsApproved = x.IsApproved,
                                     InvoiceStatus = x.InvoiceStatus,
                                     InvoicePeriodStartDateText = x.InvoicePeriodStartDate.ToShortDateString(),
                                     InvoicePeriodEndDateText = x.InvoicePeriodEndDate.ToShortDateString(),
                                     InvoicePeriodStartDate = x.InvoicePeriodStartDate,
                                     InvoicePeriodEndDate = x.InvoicePeriodEndDate
                                 }
                                 );

0

尝试使用VB.NET,重要的一点是你需要像答案中提到的那样将结果作为可枚举对象获取。

Dim _EventsDaysResult = From ED In TAdbContext.EventPolicies.AsEnumerable
                                    Where ED.EventID = EID
                                    Select New With {ED.EventID, 
                                    .DayInfo = 
                                    ED.EventDay.GetValueOrDefault.ToShortDateString & " ( " & ED.EventDayTitle & " ) "}

-3
return dbContext.Reporting_DailyNAV_Pricing.AsEnumerable().Select(x => new SelectListItem
{
    Text = x.PricingSecurityID.ToString(),
    Value = x.PricingSecurityID.ToString()
}).ToList();

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