LINQ to Entities 无法识别方法“System.String Format(System.String, System.Object, System.Object)”

Posted

技术标签:

【中文标题】LINQ to Entities 无法识别方法“System.String Format(System.String, System.Object, System.Object)”【英文标题】:LINQ to Entities does not recognize the method 'System.String Format(System.String, System.Object, System.Object)' 【发布时间】:2012-04-22 05:25:57 【问题描述】:

我有这个 linq 查询:

private void GetReceivedInvoiceTasks(User user, List<Task> tasks)

    var areaIds = user.Areas.Select(x => x.AreaId).ToArray();

    var taskList = from i in _db.Invoices
                   join a in _db.Areas on i.AreaId equals a.AreaId
                   where i.Status == InvoiceStatuses.Received && areaIds.Contains(a.AreaId)
                   select new Task 
                       LinkText = string.Format(Invoice 0 has been received from 1, i.InvoiceNumber, i.Organisation.Name),
                       Link = Views.Edit
                   ;

但它有问题。我正在尝试创建任务。对于每个新任务,当我将链接文本设置为像“Hello”这样的常量字符串时,这很好。但是上面我正在尝试使用发票的属性来构建属性链接文本。

我收到此错误:

base System.SystemException = "LINQ to Entities 无法识别方法 'System.String Format(System.String, System.Object, System.Object)' 方法,并且此方法无法转换为存储表达式。”

有人知道为什么吗?任何人都知道这样做的另一种方法来使它工作?

【问题讨论】:

是的,原来错过了 LINQ to Entities does not recognize the method 'System.String ToString()' method的可能重复 【参考方案1】:

Entity Framework 正在尝试在 SQL 端执行您的投影,其中没有与 string.Format 等效的位置。使用AsEnumerable() 强制使用 Linq to Objects 评估该部分。

基于on the previous answer我已经给你我会像这样重组你的查询:

int statusReceived = (int)InvoiceStatuses.Received;
var areaIds = user.Areas.Select(x=> x.AreaId).ToArray();

var taskList = (from i in _db.Invoices
               where i.Status == statusReceived && areaIds.Contains(i.AreaId)
               select i)
               .AsEnumerable()
               .Select( x => new Task()
               
                  LinkText = string.Format("Invoice 0 has been received from 1", x.InvoiceNumber, x.Organisation.Name),
                  Link = Views.Edit
                );

我还看到您在查询中使用了相关实体 (Organisation.Name),请确保将正确的 Include 添加到您的查询中,或者专门实现这些属性以供以后使用,即:

var taskList = (from i in _db.Invoices
               where i.Status == statusReceived && areaIds.Contains(i.AreaId)
               select new  i.InvoiceNumber, OrganisationName = i.Organisation.Name)
               .AsEnumerable()
               .Select( x => new Task()
               
                  LinkText = string.Format("Invoice 0 has been received from 1", x.InvoiceNumber, x.OrganisationName),
                  Link = Views.Edit
                );

【讨论】:

除了由于表达式树翻译导致select-new-task部分不能发生在服务器端之外,还应该注意的是,这样做是不可取的。大概,您希望在客户端创建任务。因此,查询和创建任务的分离可能会更加明确。 我还建议选择一个只包含所需 InvoiceNumber 和 Organisation.Name 的匿名类型。如果发票实体很大,那么即使您只使用两列,带有后续 AsEnumerable 的 select i 也会拉回每一列。 @Devin:是的,我同意——事实上,这正是第二个查询示例正在做的事情。【参考方案2】:

IQueryable 派生自 IEnumerable,主要的相似之处是当您进行查询时,它会以其语言发布到数据库引擎,而关键时刻是您告诉 C# 处理服务器上的数据(不是客户端)或告诉 SQL 处理数据。

所以基本上当你说IEnumerable.ToString() 时,C# 获取数据集合并在对象上调用ToString()。 但是当你说IQueryable.ToString()时,C#告诉SQL在对象上调用ToString()但是SQL中没有这样的方法。

缺点是当您在 C# 中处理数据时,您正在查看的整个集合必须在 C# 应用过滤器之前在内存中构建。

最有效的方法是将查询设为IQueryable,并使用您可以应用的所有过滤器。

然后在内存中构建它并在 C# 中进行数据格式化。

IQueryable<Customer> dataQuery = Customers.Where(c => c.ID < 100 && c.ZIP == 12345 && c.Name == "John Doe");

 var inMemCollection = dataQuery.AsEnumerable().Select(c => new
                                                  
                                                     c.ID
                                                     c.Name,
                                                     c.ZIP,
                                                     c.DateRegisterred.ToString("dd,MMM,yyyy")
                                                   );

【讨论】:

【参考方案3】:

虽然 SQL 不知道如何处理 string.Format,但它可以执行字符串连接。

如果你运行下面的代码,那么你应该得到你想要的数据。

var taskList = from i in _db.Invoices
               join a in _db.Areas on i.AreaId equals a.AreaId
               where i.Status == InvoiceStatuses.Received && areaIds.Contains(a.AreaId)
               select new Task 
                   LinkText = "Invoice " + i.InvoiceNumber + "has been received from " + i.Organisation.Name),
                   Link = Views.Edit
               ;

一旦您实际执行查询,这应该比使用 AsEnumerable 稍微快一些(至少这是我在遇到与您相同的原始错误后在自己的代码中发现的)。如果您使用 C# 做一些更复杂的事情,那么您仍然需要使用 AsEnumerable

【讨论】:

不知道为什么 Linq 不能适应使用 FORMATMESSAGE 函数docs.microsoft.com/en-us/sql/t-sql/functions/… 在此之前你的解决方案是(不强制实现) 根据数据库结构和相关列的数量,使用此方法代替AsEnumerable() 可以大大提高效率。避免使用AsEnumerable()ToList(),直到您确实想要将所有结果都记入内存。

以上是关于LINQ to Entities 无法识别方法“System.String Format(System.String, System.Object, System.Object)”的主要内容,如果未能解决你的问题,请参考以下文章

LINQ to Entities 无法识别方法 IsNullOrWhiteSpace

错误:LINQ to Entities 无法识别方法 DataLength

LINQ To Entities 无法识别方法 Last。真的吗?

LINQ To Entities 无法识别方法 Last。真的吗?

C# LINQ to Entities 无法识别方法“布尔”

我如何修复“LINQ to Entities无法识别方法”错误