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

时间:2012-04-09 21:17:15

标签: linq entity-framework linq-to-entities

我有这个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”这样的常量字符串时,它很好。但是上面我试图使用发票的属性来构建属性linktext。

我收到此错误:

  

base {System.SystemException} = {“LINQ to Entities无法识别方法'System.String Format(System.String,System.Object,System.Object)'方法,并且此方法无法转换为商店表达。“}

任何人都知道为什么?任何人都知道这样做的另一种方法是让它起作用吗?

3 个答案:

答案 0 :(得分:137)

实体框架试图在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
                });

答案 1 :(得分: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")
                                                   });

答案 2 :(得分:1)

尽管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