我该如何使用LINQ将这个父子对象模型投影到一个扁平的单一对象中?

7
我会尽力帮忙翻译中文。以下是需要翻译的内容:

我正在尝试将一个具有父 + 子数组的简单类压缩成一个单一的类。

源文:

public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<PewPew> PewPews { get; set; }
}

public class PewPew
{
    public string Name { get; set; }
    public string Whatever { get; set; }
}

- 1 | Fred | { { AAA | xxx }, { BBB | yyy } }
- 2 | Bill | { { CCC | zzz } }

致:

public class FooProjection
{
    public int Id { get; set; }
    public string Name { get; set; }
    public PewPewName { get; set; }
    public PewPewWhatever { get; set; }
}

- 1 | Fred | AAA | xxx
- 1 | Fred | BBB | yyy
- 2 | Bill | CCC | zzz
2个回答

13

纯Linq方法

你可以使用SelectMany()重载方法,该方法允许你指定一个结果选择器,它会在集合中的每个元素上调用:

将序列中的每个元素投影到一个 IEnumerable 中,将生成的序列扁平化为一个序列,并在其中的每个元素上调用一个结果选择器函数。

List<Foo> foos = new List<Foo>();
var fooProjections = foos.SelectMany(x => x.PewPews, (foo, pew) => new FooProjection() 
{ 
    Id = foo.Id, 
    Name = foo.Name,
    PewPewName = pew.Name,
    PewPewWhatever = pew.Whatever 
}).ToList();

这种方法最为简洁,但需要一些时间来适应,特别是如果你之前没有大量使用Linq的话。

编辑:

根据@AS-CII的评论,使用一个循环和一个简单的Select()投影可能更容易理解(并且这对于维护代码库非常重要)。如果在这种情况下出现了Linq问题,使用两个嵌套循环也可以解决。我将展示完整的两种方法。

使用 foreach 循环和 Select 投影

只需遍历所有的Foos,并为当前项中的每个PewPew创建一个新的FooProjection。将它们全部添加到处于作用域中的fooProjections列表中。这种方法使用Linq投影,将每个PewPew映射到一个FooProjection中,其中使用了foreach循环中的foo

List<Foo> foos = new List<Foo>();
List<FooProjection> fooProjections = new List<FooProjection>();

foreach(var foo in foos)
{
    var someFooProjections = foo.PewPews.Select(x => new FooProjection() 
    { 
        Id = foo.Id, 
        Name = foo.Name, 
        PewPewName = x.Name, 
        PewPewWhatever = x.Whatever 
    });
    fooProjections.AddRange(someFooProjections);
}

使用两个嵌套的foreach循环

只需使用两个foreach循环,外部循环迭代Foos,内部循环迭代当前Foo中的PewPews集合 - 向作用域内的fooProjections列表添加一个新的FooProjection即可。这种方法完全不使用Linq。

List<FooProjection> fooProjections = new List<FooProjection>();
foreach (var foo in foos)
    foreach (var pew in foo.PewPews)
    {
        fooProjections.Add(new FooProjection()
        {
            Id = foo.Id,
            Name = foo.Name,
            PewPewName = pew.Name,
            PewPewWhatever = pew.Whatever
        });
    }

向他展示'更多的词'的做法。这样做更加简洁易懂 :) - as-cii
@BrokenGlass,详细的方式也很好(为了比较),但我对此也非常酷。如果一些Foo具有null PewPews属性会发生什么? - Pure.Krome
添加了与纯LINQ、单个foreach循环和两个嵌套的foreach循环的比较。 - BrokenGlass
@Pure.Krome:在这三种方法中,你必须检查PewPew不为空,最简单的方法是用foos.Where(x => x.PewPews != null)替换foos - BrokenGlass
所有的LINQ版本都非常容易理解,你只需要理解LINQ。把它分解开来就像是展示“更多单词”的做法,然后再将其分解成IL代码和程序集...这是一个有效的概念,但在这种情况下,我认为LINQ已经足够好了;你必须学会这门语言。 - xr280xr

6

我发现在查询表达式中使用多个 from 比调用 SelectMany 更容易编写,它们编译后的结果是一样的。

List<Foo> foos = GetFoos();

var projected = 
    from foo in foos
    from pewPew in foo.PewPews
    select new FooProjection
        { Id = foo.Id, 
          Name = foo.Name, 
          PewPewName = pewPew.Name, 
          PewPewWhatever = pewPew.Whatever };

这也很酷,实际上:) 赞! - Pure.Krome

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接