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

时间:2009-12-30 19:48:22

标签: c# linq entity-framework join linq-to-entities

我有这个LINQ查询:

    // types...
    LinkedList<WeightedItem> itemScores = new LinkedList<WeightedItem>();

    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都包含正确的值。

3 个答案:

答案 0 :(得分:21)

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

可能工作的一件事是使用Contains代替。我不知道LinkedList<T>是否适用于此,但我相信List<T>确实如此,至少在LINQ to SQL中是这样的:

List<int> 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<int> 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<int, decimal?> map = itemScores.ToDictionary(x => x._id,
                                                        x => x._score);

var result = tmp.Select(i => new ItemSearchResult(i, map[i.Id]));

答案 1 :(得分:3)

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

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);

答案 2 :(得分:1)

以防_ctx.Items表示的表不是很大而你不关心加载 在内存中的所有表然后在内存中过滤它,您可以简单地交换项中的顺序 join语句,如下面的代码片段所示:

LinkedList<WeightedItem> itemScores = new LinkedList<WeightedItem>();

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<TResult> Queryable.Join<TOuter, TInner, TKey, TResult>(
        this IQueryable<TOuter> outer,
        IEnumerable<TInner> inner,
        Expression<Func<TOuter, TKey>> outerKeySelector,
        Expression<Func<TInner, TKey>> innerKeySelector,
        Expression<Func<TOuter, TInner, TResult>> resultSelector
)

在交换的时候,调用了Enumerable扩展方法:

IEnumerable<TResult> Enumerable.Join<TOuter, TInner, TKey, TResult>(
        this IEnumerable<TOuter> outer,
        IEnumerable<TInner> inner,
        Func<TOuter, TKey> outerKeySelector,
        Func<TInner, TKey> innerKeySelector,
        Func<TOuter, TInner, TResult> resultSelector
)

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

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

我不建议以这种方式加入,尽管它对于后台应用程序来说非常有用 涉及的表格由少量记录组成,应用程序不会受到相关性能的恶化。 毕竟,这个解决方案可以使代码更清晰。