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();

References

Tags: linq