LINQTo 实体不识别方法 Last。真的吗?

在这个查询中:

public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
var context = DataContext.GetDataContext();
return context.ServerOnlineCharacters
.OrderBy(p => p.ServerStatus.ServerDateTime)
.GroupBy(p => p.RawName)
.Select(p => p.Last());
}

我得把它换成这个才能用

public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
var context = DataContext.GetDataContext();
return context.ServerOnlineCharacters
.OrderByDescending(p => p.ServerStatus.ServerDateTime)
.GroupBy(p => p.RawName)
.Select(p => p.FirstOrDefault());
}

我甚至不能使用 p.First()来镜像第一个查询。

为什么在这样一个健壮的 ORM 系统中存在这样的基本限制?

56304 次浏览

这个限制归结为一个事实,即最终它必须将查询转换为 SQL,而 SQL 有一个 SELECT TOP(在 T-SQL 中) ,但没有一个 SELECT BOTTOM(没有这样的东西)。

有一个简单的方法绕过它,只是 顺序下降,然后做一个 First(),这就是你所做的。

编辑: 其他提供者可能会有不同的 SELECT TOP 1实现,在 Oracle 上可能会更像 WHERE ROWNUM = 1

编辑:

另一个效率较低的替代方法 -我不建议这样做!-是在 .Last()之前对数据调用 .ToList().Last()将立即执行已经构建到该点的 LINQTo 实体表达式,然后执行。Last ()将工作,因为此时 .Last()将在 对象的 LINQ表达式的上下文中有效地执行。(正如您所指出的,它可能会带回成千上万条记录,并浪费大量 CPU 物化对象,而这些对象永远不会被使用)

同样,我不建议这样做,但它确实有助于说明 LINQ 表达式在何时何地执行之间的区别。

用 Linq 选择器 OrderByDescending(x => x.ID).Take(1).Single()替换 Last()

如果你更喜欢在 Linq 中做这件事,那么这样的事情就会奏效:

public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
var context = DataContext.GetDataContext();
return context.ServerOnlineCharacters.OrderBy(p => p.ServerStatus.ServerDateTime).GroupBy(p => p.RawName).Select(p => p.OrderByDescending(x => x.Id).Take(1).Single());
}

不要用 Last(),试试这个:

model.OrderByDescending(o => o.Id).FirstOrDefault();

还有另一种方法获取最后一个元素而不使用 OrderByDesending 并加载所有实体:

dbSet
.Where(f => f.Id == dbSet.Max(f2 => f2.Id))
.FirstOrDefault();

这是因为 LINQtoEntities (以及一般的数据库)不支持所有的 LINQ 方法(请参阅此处了解详细信息: http://msdn.microsoft.com/en-us/library/bb738550.aspx)

这里您需要的是以这样一种方式对数据进行排序: “ last”记录变成“ first”,然后您可以使用 FirstOrDefault。注意,数据库通常没有“第一个”和“最后一个”这样的概念,最近插入的记录不会是表中的“最后一个”。

这个方法可以解决你的问题

db.databaseTable.OrderByDescending(obj => obj.Id).FirstOrDefault();

在选择函数之前添加单个函数 AsEnumerable()对我来说很有效。
例如:

return context.ServerOnlineCharacters
.OrderByDescending(p => p.ServerStatus.ServerDateTime)
.GroupBy(p => p.RawName).AsEnumerable()
.Select(p => p.FirstOrDefault());

参考: Https://www.codeproject.com/questions/1005274/linq-to-entities-does-not-recognize-the-method-sys