为什么这个LINQ join语句不起作用?

我有这个LINQ查询:

// types... LinkedList itemScores = new LinkedList(); var result = from i in _ctx.Items join s in itemScores on i.Id equals s._id orderby s._score descending select new ItemSearchResult(i, s._score); // this fails: return result.ToList(); 

哪个生成此错误:

无法创建类型为’System.Collections.Generic.IEnumerable`1’的常量值。
在此上下文中仅支持基本类型(例如Int32,String和Guid’)。

[编辑]这是WeightedItem的代码:

 public class WeightedItem { public int _id; public decimal? _score; public WeightedItem(int id, decimal? score) { _id = id; _score = score; } } 

你能看出我做错了什么吗? 代码编译完美,_ctx.Items和itemScores都包含正确的值。

是的,它编译得很好 – 问题是它无法将其转换为SQL。 当您引用“本地”值时,entity framework必须在需要创建SQL查询时确定如何处理它们。 它基本上无法应对内存中集合和数据库表之间的连接。

可能有用的一件事是使用Contains代替。 我不知道LinkedList是否适用于此,但我相信List ,至少在LINQ to SQL中:

 List requiredScoreIds = itemScores.Select(x => x._id).ToList(); var tmp = (from i in _ctx.Items where requiredScoreIds.Contains(i.Id) orderby s._score descending select i).AsEnumerable(); // Now do the join in memory to get the score var result = from i in tmp join s in itemScores on i.Id equals s._id select new ItemSearchResult(i, s._score); 

现在,它正在进行内存中查询的连接,这在某种程度上是不必要的。 您可以改为使用字典:

 List requiredScoreIds = itemScores.Select(x => x._id).ToList(); var tmp = (from i in _ctx.Items where requiredScoreIds.Contains(i.Id) orderby s._score descending select i).AsEnumerable(); // Create a map from score ID to actual score Dictionary map = itemScores.ToDictionary(x => x._id, x => x._score); var result = tmp.Select(i => new ItemSearchResult(i, map[i.Id])); 

您无法在内存列表和可查询对象之间进行连接。 你需要做这样的事情:

 var criteria = itemScores.Select(x => x._id).ToList(); var result_tag = (from i in _ctx.Items where criteria.Contains(i.ID) select i).ToList(); var result = from i in result_tag join s in itemScores on i.ID equals s._id orderby s._score descending select new ItemSearchResult(i, s._score); 

如果由_ctx.Items表示的表不是一个大表并且你不关心在内存中加载所有表然后在内存中过滤它,你可以简单地交换join语句中的项的顺序,如在以下代码段中:

 LinkedList itemScores = new LinkedList(); var result = from s in itemScores join i in _ctx.Items on s._id equals i.Id orderby s._score descending select new ItemSearchResult(i, s._score); return result.ToList(); 

在原始语句中,调用了Queryable扩展方法:

 IQueryable Queryable.Join( this IQueryable outer, IEnumerable inner, Expression> outerKeySelector, Expression> innerKeySelector, Expression> resultSelector ) 

而在交换的一个中,调用了Enumerable扩展方法:

 IEnumerable Enumerable.Join( this IEnumerable outer, IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector ) 

所以在最后一个语句中,完整的_ctx.Items表被加载到内存中,然后通过Linq到Objects连接到itemScores列表(我不知道LinkedList,我用List尝试过)。

我添加这个答案主要是因为有人可以按相反的顺序键入连接并使其工作,甚至没有意识到数据库中会发生什么。

我不建议以这种方式加入,但是只要涉及的表由少量记录组成并且应用程序不会使相关性能恶化,它对后台应用程序非常有用。 毕竟,这个解决方案可以使代码更清晰。