LINQ – Projecting Properties into Lists
I had a bit of code that iterated through the database, looked for distincts in a certain column, and then added those values into a List<int>. That seemed a bit, well, overworked, for simply populating a drop down list.
Why do I need a separate List object? Why not simply compose it from my Select?
Original code:
public List<int> GetDistinctYears()
{
var result =
this.SelectAll()
.Where(i => i.SchoolYear == School.CurrentSchoolYear)
.Distinct();
var years = new List<int>();
foreach (var v in result)
{
years.Add(v.SchoolYear);
}
return years;
}
I don’t like that. Additional loops just slows down the code.
The problem is in the LINQ statement and getting it right.
FAIL: Creating an anonymous type will fail because the compiler cannot convert it into a List<int>.
.Select(x => new { SchoolYear = x.SchoolYear } )
FAIL: You also can’t specify the type explicitly on creation because it tries to create an IEnumerable.
.Select(x => new int { x.SchoolYear } )
PASS: You can, however, specify exactly what you’re intending—an array—and using SelectMany instead of Select.
.SelectMany(x => new int[] { x.SchoolYear } )
PASS: You can also project the property itself (without newing it up), it’ll automatically be wrapped up into an IQueryable<T>.
.Select(x => x.SchoolYear)
Now, our new code looks a bit cleaner and should be faster without those excessive foreach loops. This also converts our IQueryable<T> to a List<T>, or List<int> in this case.
Updated code:
return this.SelectAll()
.Where(i => i.SchoolYear == School.CurrentSchoolYear)
.Select(x => x.SchoolYear)
.Distinct()
.ToList();
NOTE: “Order of Operations” is extremely important when using Distinct—remember to complete all of your selecting BEFORE distincting.
WHERE > SELECT > DISTINCT/GROUP > ORDER > CASTS. 🙂