2012-04-09 91 views
59

我有这样的LINQ查询:LINQ到实体无法识别方法 'System.String格式(System.String,System.Object的,System.Object的)'

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 does not recognize the method 'System.String Format(System.String, System.Object, System.Object)' method, and this method cannot be translated into a store expression."}

任何人都知道为什么吗?任何人都知道这样做的另一种方式,使其工作?

+0

是,错过了这一点原本 – AnonyMouse 2012-04-09 21:21:58

+0

可能重复[LINQ到实体无法识别方法“System.String的ToString()”方法(http://stackoverflow.com/questions/4121863/linq- to-entities-does-not-recognized-the-method-system-string-tostring-method) – 2013-12-12 13:32:23

回答

115

实体框架试图在SQL端执行您的投影,其中没有等效于string.Format。使用AsEnumerable()来强制用Linq对对象评估该部分。

基于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 
       }); 
+0

除了由于表达式树翻译而导致select-new-task部分不能发生服务器端的事实之外,它也应该是指出这样做是不可取的。据推测,你想要创建客户端的任务。因此,查询和创建任务的分离可能更加明确。 – Tormod 2012-04-09 21:54:06

+3

我还建议选择一个匿名类型,它只需要其中的InvoiceNumber和Organisation.Name。如果发票实体很大,那么使用随后的AsEnumerable选择我将拉回每一列,即使您只使用两个。 – Devin 2012-04-10 21:45:07

+1

@Devin:是的,我同意 - 事实上,这正是第二个查询示例正在做的事情。 – BrokenGlass 2012-04-10 21:47:47

14

IQueriable自IEnumerable派生,主要的相似之处是,当你让你的查询其发布到数据库引擎在它的郎在这个时刻,你可以告诉C#处理服务器上的数据(而不是客户端)或告诉SQL处理数据。

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

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

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

然后在内存中构建它并在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") 
                }); 
相关问题