我有这个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)'方法,并且此方法无法转换为商店表达。“}
任何人都知道为什么?任何人都知道这样做的另一种方法是让它起作用吗?
答案 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
。