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


88

我有这个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)',并且该方法无法转换为商店表达式。” }

有人知道为什么吗?有人知道这样做的另一种方法以使其起作用吗?


是的,最初错过了
AnonyMouse 2012年

Answers:


148

实体框架正在尝试在SQL端执行您的投影,而在SQL端没有等效项string.Format。使用AsEnumerable()通过Linq to Objects强制评估该零件。

根据先前给出的答案,我将像这样重组查询:

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
                });

除了由于表达式树转换而导致选择新任务部分无法在服务器端发生这一事实之外,还应注意,这样做是不可取的。大概您希望在客户端创建任务。因此,查询和任务创建的分离可能更加明确。
Tormod

3
我还建议选择一个仅包含所需InvoiceNumber和Organisation.Name的匿名类型。如果发票实体很大,那么即使您仅使用两列,带有后续AsEnumerable的select i也会拉回每一列。
Devin 2012年

1
@Devin:是的,我同意-实际上,这正是第二个查询示例正在执行的操作。
BrokenGlass 2012年

15

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


2
不知道为什么Linq无法适应使用FORMATMESSAGE函数docs.microsoft.com/en-us/sql/t-sql/functions/… 直到那时您才是解决方案(无需强制实现)
MemeDeveloper

2
根据数据库的结构和相关列的数量,使用此方法AsEnumerable()可以大大提高效率。避免这样做AsEnumerable()ToList()直到您确实希望将所有结果都记入内存。
克里斯·谢勒
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.