How can I use LINQ to project this parent and children object model into a flat, single object?
I'm trying to flatten out a simple class that has a parent + child array, into a single class.
From:
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开发者_JAVA技巧 | Bill | { { CCC | zzz } }
To:
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
The pure Linq approach
You can use the SelectMany()
overload that allows you to specify a result selector that is called on every element in the collection:
Projects each element of a sequence to an IEnumerable, flattens the resulting sequences into one sequence, and invokes a result selector function on each element therein.
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();
This approach is the most concise, but takes some time getting used to, especially if you have not been working with Linq a lot.
Edit:
As per @AS-CII's comment it might be more readily understandable (and this is important for maintaining the code base) to just use a loop and a simple projection with Select()
. If someone has a problem with Linq in this scenario at all, two nested loop would do as well. I'll show both for completeness.
Using a foreach loop and a Select projection
Just iterate over all Foos and create a new FooProjection
for each PewPew
in the current item. Add all of them to the fooProjections
list that is in scope. This approach uses a Linq projection to map from each PewPew
to a FooProjection
, using the foo
from the foreach loop.
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);
}
Using two nested foreach loops
Just use two foreach
loops, the outer iterating over the Foos, the inner over the PewPews collection in the current foo - add a new FooProjection
to the fooProjections
list that is in scope. This approach does not make use of Linq at all.
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
});
}
I find query expressions with multiple from
s far easier to write than calls to SelectMany
. They compile to the same thing.
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 };
精彩评论