LINQ Reference
October 31, 2017
The benefits of using the LINQ approach rather than lots of foreach
loops are:
- Composability
- Lazy Evaluation
- Immutability
- Parallelizable
- Declarative
The functions available in LINQ can be split into four categories:
- Generator Functions.- create values out of nothing
- Statistical Functions - find out stats about the data (count, any that match this condition)
- Projector Functions - Take one collection and return another
- Filters - Produce a subset of a collection
Generator Function Examples
Create a sequence of ten ones:
var lTenOnes = Enumerable.Repeat(1, 10);
Create a sequence of the numbers one to ten:
var lOneToTen = Enumerable.Range(1, 10)
Statistical Function Examples
To find out whether there is an item in the set that matches a condition (IsAccepted
is defined as a bool?
hence we need to check if it has a value before fetching the Value
):
bool lHasAccepted = ItemsList.Any(itm => itm.IsAccepted.HasValue && itm.IsAccepted.Value == true);
Count the items in a set that matches a condition:
int lMatchesCount = ItemsList.Any(itm => itm.IsAccepted.HasValue && itm.IsAccepted.Value == true).Count();
Return the first row from a result set:
var lMatchingCustomer = this.dbContext.Customers.Where(cust => cust.id ==5).First();
If you're only expecting one row to be returned, it's better to use Single()
instead. This will raise an exception if the query returns multiple rows, rather than just discarding the ones after the first:
var lMatchingCustomer = this.dbContext.Customers.Where(cust => cust.id ==5).Single();
SingleOrDefault
and FirstOrDefault
are both available. These won't raise an exception if there Where
clause doesn't match, but will return an empty instance of the object.
Projector Function Examples
Compute the squares of the numbers one to ten:
IEnumerable<int> lSquares = Enumerable.Range(1, 10).Select(x => x * x);
Convert a list of one type to a list of another
return await this.dbContext.Submissions.Where(sub => sub.Id == pSubmissionId).Select(sub => new SubmissionResultsDto { DisplayName = sub.SalonYear.Salon.Name + " - " + sub.SalonYear.Name + " (" + sub.SalonYear.Year + ")", PersonId = sub.PersonId, SubmissionId = sub.Id, Entries = sub.Entries.Select(ent => new SubmissionResultsEntryDto { Id = ent.Id, Score = ent.Score, IsAwarded = ent.IsAwarded, IsAccepted = ent.IsAccepted, ImageName = ent.Image.Name, AwardDetails = ent.AwardDetails }) }).FirstOrDefaultAsync();
Submission has a list of Entries as an attribute in LINQ to SQL. Entries don't know about their Submission at the class level. I wanted to count the total number of entries, where the submission has not been judged. So we're starting off from the DB Context object to get Submissions, then summing up the total number of entries across all matching submissions.
int lUnjudgedEntryCount = await this.dbContext.Submissions.Where(sub => sub.IsJudged == false).SelectMany(sub => sub.Entries).CountAsync();
Filter Examples
Filter the items in a list and then count them:
int lAcceptedCount = ItemsList.Where(itm => itm.IsAccepted.HasValue && itm.IsAccepted.Value == true).Count();
Ordering
Select the latest news item:
var lLatestNewsItem = this.dbContext.NewsItems.OrderByDescending(ni => ni.AddedDate).First();