使用LINQ将三个列表连接成一个时会抛出异常。

10

好的,我可能做了一些愚蠢的事情,但这不应该有效吗?我有以下三个列表:

var commonViews = (from v in context.TPM_VIEWS where v.VIEWID < 0 select v); // IQueryable<TPM_VIEWS>
var ownedViews = (from v in context.TPM_VIEWS where v.OWNERID == userId && v.VIEWID > 0 select v); // IQueryable<TPM_VIEWS>
var sharedViews = (from v in context.TPM_USER.Include("TPM_VIEWS2") where v.USERID == userId select v).First().TPM_VIEWS2; // EntityCollection<TPM_VIEWS>
每个列表都有恰当的值和计数。我可以返回其中任何一个列表:
return commonViews.ToList();

我可以返回这些列表中的任意两个

return commonViews.Concat(ownedViews).ToList();

不过,当我尝试返回全部三个时:

return commonViews.Concat(ownedViews).Concat(sharedViews).ToList();

我收到了这个异常:

无法创建类型为'Entity.TPM_VIEWS'的常数值。在此上下文中,仅支持基元类型或枚举类型。

我做错了什么?这三个值都是可以枚举的。主要是因为我问这个问题是最好的办法来确保我发布后30秒内就能注意到这个问题。

更新:

我93%确定问题出在这里:

var sharedViews = (from v in context.TPM_USER.Include("TPM_VIEWS2") where v.USERID == userId select v).First().TPM_VIEWS2;

这似乎是一个可枚举的TPM_VIEWS对象列表,我可以在其上调用ToList()并获得正确的数据,但它与其他列表不兼容。

更新2:

实际上这样可以行得通。能告诉我为什么吗?

commonViews.ToList().Concat(ownedViews.ToList()).Concat(sharedViews.ToList()).ToList();

3
@JerKimball - 把$5放在SLaks身上通常是一个安全的赌注,但我认为还有其他一些问题。 - Mike Christensen
你能在每个 Enumerable 上调用 ToList() 并正确地连接列表吗? - Kevin
@Kevin - 是的,如果我在每个LINQ表达式的末尾添加.ToList()(从而使查询材料化),我就可以聚合。这不是一个的解决方案,但它仍然让我有点困惑。这一定是Entity Framework正在尝试一次性材料化整个表达树所做的一些奇怪的事情。 - Mike Christensen
ToList(); 不是将 IQueryable 转换为 IEnumerable,以便进行 concat 操作吗? - Lotok
1
可以尝试使用.AsEnumerable()代替.ToList(),这将强制使用简单的LINQ操作符,而不是生成大量的EF IQueryable。应该具有与.ToList()相同的效果,但在内存使用方面要更高效。 - Cory Nelson
显示剩余8条评论
2个回答

5
问题是在 EF 的 IQueryable<T> 上调用 Concat() 会将整个连接转换为单个查询。当您调用 .Concat(sharedViews) 时,您正在传递一个标量 (预加载) 嵌套实体类的集合。
EF 不知道如何将其转换为查询,因此它会出现错误。您可以通过调用 AsEnumerable() 而不是 ToList() 来使其更快。

太好了,我认为这很好地概括了一切! - Mike Christensen
@MikeChristensen会得到5美元吗? - Kevin

1

This actually works. Points to the person who can tell me why!

commonViews.ToList().Concat(ownedViews.ToList()).Concat(sharedViews.ToList()).ToList();
这是因为每个原始查询都是单独执行的;你只是在内存中连接结果。当您组合这3个查询时,Entity Framework查询转换器似乎存在错误,但是当您在每个查询上调用ToList时,它们不再是EF查询,而是列表,因此使用Linq to Objects进行连接。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接