【问题标题】:Why won't this LINQ join statement work?为什么这个 LINQ join 语句不起作用?
【发布时间】:2009-12-30 19:48:21
【问题描述】:

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

【问题讨论】:

  • 能否贴出WeightedItem的代码
  • 显然 WeightedItem 不是原始类型。
  • 拉撒路,完成了。 DOK,什么意思?

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


【解决方案1】:

是的,它编译得很好——问题是它不能将它翻译成 SQL。当您引用“本地”值时,实体框架必须确定在需要创建 SQL 查询时如何处理它们。它基本上无法应对在内存中的集合和数据库表之间进行连接。

可能起作用的一件事是改用Contains。我不知道LinkedList&lt;T&gt; 是否可以解决这个问题,但我相信List&lt;T&gt; 可以,至少在 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]));

【讨论】:

  • 是有道理的,所以 .AsEnumerable() 执行查询并将结果保存在内存中?如果不是,代码的哪一部分是做什么的?
  • @Mickel: AsEnumerable 不会立即执行查询 - 但它返回 IEnumerable&lt;T&gt; 而不是 IQueryable&lt;T&gt;,因此查询的其余部分将使用 Enumerable.xxx 而不是Queryable.xxx。当该查询最终需要执行时,它将在数据库中执行第一部分,在内存中执行第二部分。
【解决方案2】:

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

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

【讨论】:

  • 哎呀 - Jon Skeet 打败了我 :)
  • Jon Skeet 是 StackOverflow 的 Chuck Norris
【解决方案3】:

以防万一_ctx.Items表示的表不是很大,你不关心加载 内存中的所有表,然后在内存中对其进行过滤,您可以简单地交换项目的顺序 join 语句,如下面的 sn-p:

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 to Objects,到 itemScores 列表(我不知道 LinkedList,我用 List 试过)。

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

我不建议以这种方式加入,尽管它对后台应用程序很有用 所涉及的表由少量记录组成,应用程序不会遭受相关的性能恶化。 毕竟,这种解决方案使代码更干净。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多