【问题标题】:Select multiple records based on list of Id's with linq根据带有 linq 的 Id 列表选择多条记录
【发布时间】:2013-05-29 21:51:44
【问题描述】:

我有一个列表,其中包含我的 UserProfile 表的 ID。我如何使用LINQ 根据我在var 中获得的ID 列表选择所有UserProfiles

var idList = new int[1, 2, 3, 4, 5];
var userProfiles = _dataContext.UserProfile.Where(......);

我被困在这里了。我可以使用 for 循环等来做到这一点。但我宁愿用LINQ 来做到这一点。

【问题讨论】:

  • 搜索和查找是两件不同的事情。但是既然你可以通过互联网查看我的肩膀,你能告诉我你是怎么知道我没有搜索的吗?等等不说!你没看错吧?我的观点完全正确。
  • 提问比搜索花费更多时间。下次只需假设“他/她”进行了一次搜索或 10 次。
  • 这仍然引起了相当多的关注,所以我想我会提到 ReSharper 在建议可以将迭代代码转换为 LINQ 语句的地方方面做得非常好。对于刚接触 LINQ 的人来说,它可能是单独用于此目的的不可或缺的工具。

标签: c# linq


【解决方案1】:

您可以为此使用Contains()。当您真正尝试生成 IN 子句时,它会感觉有点倒退,但应该这样做:

var userProfiles = _dataContext.UserProfile
                               .Where(t => idList.Contains(t.Id));

我还假设每个 UserProfile 记录将有一个 int Id 字段。如果不是这种情况,您将不得不相应地进行调整。

【讨论】:

  • 嗨,是的,用户配置文件记录包含 id。所以不知何故我会做类似 t => t.id == idList.Contains(id) 的事情?
  • Contains() 将处理每个 id 值的相等性检查,如果您按照我在答案中所写的那样使用它。当您尝试将一组(数组)的项目与另一组(数据库表)进行比较时,您不必在任何地方显式写入 ==
  • 嗯,问题是t持有UserProfile的整个对象,而idList只包含int。编译器抱怨了一些事情,但我已经设法修复它。谢谢。
  • @Yuck - 对我不起作用,说函数超时!已禁用延迟加载但仍然失败。
  • 我得到“无法将 lambda 表达式转换为类型 'int',因为它不是委托类型”。如何解决这个问题?
【解决方案2】:

使用 .Where 和 .Contains 的解决方案具有 O(N 平方) 的复杂度。简单的 .Join 应该有更好的性能(由于散列,接近 O(N))。所以正确的代码是:

_dataContext.UserProfile.Join(idList, up => up.ID, id => id, (up, id) => up);

现在是我的测量结果。我生成了 100 000 个用户配置文件和 100 000 个 ID。加入用了 32 毫秒,而 .Where 用 .Contains 用了 2 分 19 秒!我在这个测试中使用了纯 IEnumerable 来证明我的说法。如果使用 List 而不是 IEnumerable,.Where 和 .Contains 会更快。无论如何,差异是显着的。最快的 .Where .Contains 是使用 Set。这一切都取决于 .Contains 的基础集合的复杂性。查看this post 了解 linq 复杂性。查看下面我的测试示例:

    private static void Main(string[] args)
    {
        var userProfiles = GenerateUserProfiles();
        var idList = GenerateIds();
        var stopWatch = new Stopwatch();
        stopWatch.Start();
        userProfiles.Join(idList, up => up.ID, id => id, (up, id) => up).ToArray();
        Console.WriteLine("Elapsed .Join time: {0}", stopWatch.Elapsed);
        stopWatch.Restart();
        userProfiles.Where(up => idList.Contains(up.ID)).ToArray();
        Console.WriteLine("Elapsed .Where .Contains time: {0}", stopWatch.Elapsed);
        Console.ReadLine();
    }

    private static IEnumerable<int> GenerateIds()
    {
       // var result = new List<int>();
        for (int i = 100000; i > 0; i--)
        {
            yield return i;
        }
    }

    private static IEnumerable<UserProfile> GenerateUserProfiles()
    {
        for (int i = 0; i < 100000; i++)
        {
            yield return new UserProfile {ID = i};
        }
    }

控制台输出:

经过。加入时间:00:00:00.0322546

Elapsed .Where .包含时间:00:02:19.4072107

【讨论】:

  • 你能用数字支持吗?
  • 我在使用 LINQ2SQL 数据上下文时收到 Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator. 错误。
  • 这个答案不太可能在 Linq2Sql 或 Linq2Entities 中给出类似的 O(N * N) 与 O(N) 改进,这就是他提出的问题/示例。查询数据库时,Linq 会自动生成带有“join”或“in()”子句的 SQL,因此它不是 O(N*N)。
  • @Yustme - 性能始终是一个考虑因素。 (我讨厌成为“这应该是公认的答案”的人,但是......)
  • 哇,非常感谢您的回答。实际上应该清楚的是,加入所需的数据比通过一次又一次地迭代比较两个列表更有效......但有时需要时间来了解想法和正确的语法。谢谢你?
【解决方案3】:

上面的答案很好,但不要忘记一件事重要 - 它们提供不同的结果!

  var idList = new int[1, 2, 2, 2, 2]; // same user is selected 4 times
  var userProfiles = _dataContext.UserProfile.Where(e => idList.Contains(e)).ToList();

这将从数据库返回 2 行(如果您只想要一个不同的排序用户列表,这可能是正确的)

但是在许多情况下,您可能需要一个未排序的结果列表。您总是必须像考虑 SQL 查询一样考虑它。请参阅 eshop 购物车示例以说明发生了什么:

  var priceListIDs = new int[1, 2, 2, 2, 2]; // user has bought 4 times item ID 2
  var shoppingCart = _dataContext.ShoppingCart
                     .Join(priceListIDs, sc => sc.PriceListID, pli => pli, (sc, pli) => sc)
                     .ToList();

这将从数据库返回 5 个结果。在这种情况下使用“包含”是错误的。

【讨论】:

    【解决方案4】:

    这应该很简单。试试这个:

    var idList = new int[1, 2, 3, 4, 5];
    var userProfiles = _dataContext.UserProfile.Where(e => idList.Contains(e));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-16
      相关资源
      最近更新 更多