问题描述
我遇到关于LINQ to Entities does not recognize the method 'System.String Format
的错误,但是过去我可以在包含.AsEnumerable()
的情况下执行此操作,因为GroupBy
部分需要做一些不同的事情?
select new PresentationLayer.Models.PanelMeeting
{
GroupId = pg.GroupId,MeetingId = pmd.MeetingId,GuidelineName = pmv.GuidelineName,PaneldisclosuresAttendanceURL = string.Format("{0}?MeetingId={1}&GroupId=0",PaneldisclosureLink,pmd.MeetingId),}).GroupBy(g => new
{
g.MeetingId,g.GroupId
})
.AsEnumerable()
.SelectMany(grp => grp.AsEnumerable()).ToList(),
解决方法
如果要使用string.Format
,首先必须从服务器获取数据。
您可以将.GroupBy( ... )
,然后将.AsEnumerable()
调用移到顶部,然后移到select new PresentationLayer.Models.PanelMeeting { ... }
之前。如果您不是那样选择太多数据的话...
您必须了解IEnumerable<...>
和IQueryable<...>
之间的区别。
IEnumerable
实现IEnumerable<...>
的对象表示一系列相似的项。您可以要求序列中的第一个元素,只要您有元素,就可以要求下一个元素。 IEnumerable对象应该在自己的进程中执行。 IEnumerable对象包含一切以枚举序列。
在最低级别,使用GetEnumerator()
/ MoveNext()
/ Current
完成此操作:
IEnumerable<Customer> customers = ...
IEnumerator<Customer> enumerator = customers.GetEnumerator();
while (enumerator.MoveNext())
{
// There is a next Customer
Customer customer = enumerator.Current;
ProcessCustomer(customer);
}
如果使用foreach,则在内部调用GetEnumerator / MoveNext / Current。
如果您仔细观察LINQ,您会发现有两组LINQ方法。返回IEnumerable<TResult>
的那些和不返回IEnumerable<...>
的那些
LINQ函数不会枚举查询。他们使用延迟执行或延迟执行。在每种LINQ方法的注释部分,您将找到此描述。
另一组的LINQ函数将执行查询。如果查看reference source of extension class Enumerable,您会发现它们在内部使用foreach,或者在较低级别使用GetEnumerator / MoveNext / Current
IQueryable
实现IQueryable<...>
的对象看起来像IEnumerable
。但是,它表示获取可枚举序列数据的潜力。数据通常由不同的过程提供。
为此,IQueryable拥有一个Expression
和一个Provider
。表达式表示必须以某种通用格式获取的内容。提供者知道谁将提供数据(通常是数据库管理系统)以及如何与该DBMS通信(通常是SQL)。
当您开始枚举序列时,使用GetEnumerator深入内部,则将表达式发送给提供程序,该提供程序将尝试将其转换为SQL。从DBMS提取数据,并将其作为Enumerable对象返回。通过重复调用MoveNext / Current来访问获取的数据。
由于在开始枚举之前未与数据库联系,因此必须保持与数据库的连接处于打开状态,直到完成枚举为止。您可能曾经犯过以下错误:
IQueryable<Customer> customers;
using (var dbContext = new OrderDbContext(...))
{
customers = dbContext.Customers.Where(customer => customer...);
}
var fetchedCustomers = customers.ToList();
回到您的问题
在查询中,您使用string.Format(...)
。您的提供程序不知道如何将此方法转换为SQL。您的提供者也不知道您的任何本地方法。实际上,LINQ对实体甚至不支持几种标准的LINQ方法。参见Supported and Unsupported LINQ methods。
如何解决问题?
如果需要调用不受支持的方法,则可以使用AsEnumerable
来获取数据。 AsEnumerable
之后的所有LINQ方法都由您自己的进程执行。因此,您可以调用任何自己的函数。
数据库管理系统在表处理方面进行了优化。数据库查询的最慢部分之一是将所选数据传输到本地进程。因此,让DBMS进行所有选择,并尝试传输尽可能少的数据。
因此,让您的DBMS执行Where /(组-)加入/求和/ FirstOrDefault /任何其他操作。字符串格式设置最适合您。
在您的String.Format中,使用PanelDisclosureLink和pmd.MeetingId。如果您的进程进行格式化,可能会更快。 las,您忘记给我们开始或提出疑问。
我不确定您的PanelDisclosureLink来自何处。它是局部变量吗?如果是这种情况,那么PanelDisclosuresAttendanceURL将是组中每个项目的相同字符串。这是故意的吗?
var panelDisclosureLine = ...;
var result = dbContext... // probably some joining with Pgs,Pmds and Pmvs,.Select(... => new
{
GroupId = pg.GroupId,MeetingId = pmd.MeetingId,GuidelineName = pmv.GuidelineName,})
// make groups with same combinations of [MeetingId,GroupId]
.GroupBy(joinResult => new
{
MeetingId = joinResult.MeetingId,GroupId = joinResult.GroupId,},// parameter resultSelector: use the Key,and all JoinResult items that have this key
// to make one new:
(key,joinResultItemsWithThisKey) => new
{
MeetingId = key.MeetingId,GroupId = key.GroupId,GuideLineNames = joinResultsItemsWithThisKey
.Select(joinResultItem => joinResultItem.GuideLineName)
.ToList(),})
因此,到目前为止,DBMS已将您的联接结果转换为具有 [MeetingId,GroupId]组合以及已属于的所有GuideLineName的列表 此[MeetingId,GroupId]组合。
现在,您可以将其移至本地进程并使用String.Format。
.AsEnumerable()
.SelectMany (fetchedItem => fetchedItem.GuideLineNames,(fetchedItem,guideLineName) => PresentationLayer.Models.PanelMeeting
{
GroupId = fetchedItem.GroupId,MeetingId = fetchedItem.MeetingId,GuidelineName = guidelineName,PanelDisclosuresAttendanceURL = string.Format("...",PanelDisclosureLink,fetchedItem.MeetingId);
注意:在我的参数选择中,复数是集合;单数是这些集合的元素。
PanelDisclosuresAttendanceURL = string.Format("{0}?MeetingId={1}&GroupId=0",pmd.MeetingId),}).
.GroupBy