将 Foreach 转换为 linq 表达式

Posted

技术标签:

【中文标题】将 Foreach 转换为 linq 表达式【英文标题】:Convert Foreach into linq Expression 【发布时间】:2022-01-01 04:03:00 【问题描述】:

我正在尝试从Device 表中获取针对当前客户的设备列表。我做到了,但使用了 foreach 循环,但我想使用 linq 来进行操作。

这是我的班级结构

public class Device

    public string type  get; set; 
    public int id  get; set; 
    public List<Role> roles  get; set; 



public class Role

    public int roleId  get; set; 
    public string roleName  get; set; 
    public int customerId  get; set; 

这是我的代码

var devList = list.Where(x => x.type == "device1").ToList();

foreach (var item in devList)

    if (item.roles != null)
    
        var kss = item.roles.Where(z => z.customerId == kunId).FirstOrDefault();
        if (kss != null)
        
            m.devicesList.Add(item);
        
    

我需要针对当前客户获取设备,因此我必须将我当前的用户 ID 与 customerId 进行比较。

如何将其转换为 linq-to-sql?

【问题讨论】:

【参考方案1】:

有两种方法可以解决这个问题。

首先,您可以将所有内容放入复杂的Where 操作中:

var devices = list.Where(d => d.type == "device1" && d.roles is object && d.roles.Any(r => r.customerId == kunId) );

其次,您可以将其分散到多个单独的操作中,但每个操作都更容易理解:

var devices = list.Where(d => d.type == "device1").
                   Where(d => d.roles is object).
                   Where(d => d.roles.Any(r => r.customerId == kunId) );

任一选项的性能将相似。额外的Where() 调用会产生开销,但并没有你想象的那么多...... JITter 可以用这种代码做一些惊人的事情,看到这两个结果我不会感到惊讶完全相同的 IL。

还要注意在这两种情况下都没有ToList() 调用。通过更长时间地使用IEnumerable,您通常可以大大提高性能和内存使用率。

此时,我不清楚您是否只是附加到现有的 m.devicesList 或这些值是否可以替换该集合中当前可能存在的任何内容。

假设m.devicesList 被声明为List&lt;Device&gt;(或类似),前一个选项将如下所示:

m.devicesList.AddRange(devices);

同样,没有必要曾经致电ToList()

后者看起来像这样:

m.devicesList = devices.ToList();

我们还可以将这两个步骤合并到一个语句中。以下是四种可能的组合之一:

m.devicesList.AddRange(list.Where(d => d.type == "device1").
                            Where(d => d.roles is object).
                            Where(d => d.roles.Any(r => r.customerId == kunId) )
                      );

【讨论】:

删除了我的评论和我的回答;如果检查添加项目,则错过了内部。你的好多了。 非常感谢您提供这个具有正确解释的惊人解决方案。 @joel-coehoorn @Joel Coehoorn 这完全不相关,但你的头像很好。【参考方案2】:

所以你有一个对象kunId,它与Role.CustomerId 的类型相同。您在对象devList 中还有一个Items 序列。最后,你还有一个属性m.DevicesList,它实现了ICollection&lt;Item&gt;(=你有一个方法Add(Item)

您当前的方法检查您的项目序列中的每个Item。如果属性Roles 为空,则忽略该项目。另一方面,如果您有一些角色,您将获得第一个或默认角色,其属性CustomerId 的值等于 kunId。如果有这样的非默认角色,那么您将项目添加到m.DevicesList

我将重新表述这个要求。

当且仅当 Item 具有属性 Roles 的非空值,并且这些角色中的至少一个角色具有等于 kunId 的属性 CustomerId 的值,然后您将项目添加到序列中m.DevicesList中的项目数

var itemsToAddToMDevicesList = devList.Where(item => 
    item.Roles != null &&
    item.Roles.Where(role => role.CustomerId == kunId).Any();

换句话说:您要添加到m.DeviceListItems 的序列是devList 中所有Items 的序列,其中属性Roles 具有非空值,并且至少具有属性 Roles 中的一个 Role,其属性 CustomerId 的值等于 kunId

【讨论】:

【参考方案3】:

始终尝试首先用简单的自然语言“实现”您的要求。

您需要什么设备?那些:

    类型为“device1” 具有分配给给定客户的角色

分别构建每个部分,添加必要的防护,例如roles != null 你会得到这样的结果:

var devicesFound = list.Where(d => 
   d.type == "device1" && 
   d.roles != null && 
   d.roles.Any(r => r.customerId == kunId));

m.devicesList.AddRange(devicesFound);

【讨论】:

【参考方案4】:

您应该能够删除分配给 devList 的 linq 表达式值末尾的 .ToList()。

此外,如果您的目标是优化您正在执行的操作,您可能会考虑维护当前代码,而不是将其转换为 linq 表达式。

不过,如果您需要转换 foreach 循环,请查看 official microsoft documentation,您应该能够在智能感知的帮助下自动完成。

【讨论】:

请添加一些关于您提到的性能方面的解释。 @cly ToList() 通常会浪费 CPU 和内存。最好编写代码以坚持使用更简单的 IEnumerable,并在最后一刻保存任何 ToList() 调用。此外,用于 linq 操作的状态机有时会产生内存分配开销。如果您有良好的非 linq 代码,它有时可以胜过等效的 linq,尤其是在云 Web 或虚拟 Web 服务器等内存受限的环境中。 @joel-coehoorn 所以我使用的 foreach 代码比 linq 查询更好?性能也更好?仅仅因为行数我想使用 linq 而不是 foreach。 @JoelCoehoorn 避免ToList() 对我来说也是微不足道的。关于 LINQ 与非 LINQ 代码的陈述是我感兴趣的。开发团队承诺自 LINQ 首次发布以来,它的性能会得到如此多的改进,这就是为什么你在这里所说的关于状态机和其他内容的内容很有趣。强调“拥有好的非LINQ代码”你当然是对的。 @JoelCoehoorn,感谢您的解释。在内存管理需要循环的环境中,尤其是在微服务架构中,尽可能高效是关键。

以上是关于将 Foreach 转换为 linq 表达式的主要内容,如果未能解决你的问题,请参考以下文章

LINQ to nHibernate - 将 SQL“NOT IN”表达式转换为 LINQ

将 SQL 转换为 LINQ 方法表达式

将 OData Uri 转换为等效的 Linq 表达式

如何将带有内连接的 sql 查询转换为 linq lambda 表达式?

如何以编程方式将 LINQ 查询转换为正确描述 linq 表达式的可读英文文本?

将多级 for 循环转换为 linq 或 lambda 表达式