LINQ Notes

Reference notes taken while going through the PluralSight LINQ Fundamentals course:

Yield

  • is a continuation
  • yields it'self back to whoever called it

where

  • where is lazy

Greedy operator

  • .ToList() --> whatever is coming in, I'm going to make a concrete list of this and then return it. So if you add the .ToList() to the end of a linq expression, then it is no-longer lazy loading

  • If you do not add a greedy operator, then you automatically get deferred execution in linq

  • ToList() - Greedy

  • Count() - Greedy - returns an int - something concrete, so it returns a number

Streaming Lazy Operator

  • custom where method that we implemented
  • it doesn't have to look any further into the connection then it needs to in order to match the predicate eg: with the yield return
  • where is lazy and streaming
  • orderby - has to look through all of the objects that are in that sequence
  • select - is a lazy streaming operator

let keyword

  • allow you to re-use an expression eg:
var query =
	from e in repository.GetAll()
	let lname = e.Name.ToLower()
	where lname = "scott"
	select lname;
  • you can then use lname instead of having to keep casting Name.ToLower()

into keyword

  • use the into keyword to continue a query after a projection.
    common use of into is for grouping the first range variable that you will have will go out of scope

eg:

var employees = 
from employee in repository.GetAll()
where employee.Name.StartsWith("P")
select employee
	into pEmployee
	where pEmployee.Name.Length < 5
	select pEmployee;

Grouping

  • in SQL - you get a flat record set back
  • eg: get a count of employees by departmentId
  • in linq -> groupby actually returns a higherarchical dataset

Projection

		select new {};

Joints with LINQ

LEFT JOIN in SQL Server

  • Always bring back the left hand side of a join, even if it doesn't have a match on the right hand side so if we were joining authors into books and an author hasn't written that first book yet, we would still get that author, we would just have null values for any of the book columns that we selected
  • eg: do to a left join in LINQ, you need to use the into keyword after the join
  • when you use the into keyword, you get back a grouping
  • a group join is a left join. You need to do the join first and then use the into keyword
  • a join is just an inner join
  • If you want to do a cross join (for example for completenes, then just skip the join keyword all together), and use 2 FROMs eg:
  • from: load up all the employees
  • from: load up all the departments, then create a new projection

Standard LINQ Query Operators

Streaming

  • work on infanite ienumberable input because they can go 1 element at a time: eg: select operator

Non-Streaming

  • have to do more work
  • eg: orderby operator
  • when I ask it for the first result, it has to scan through everything in the pipline before it can produce a result
  • orderby, select, join, grouping -- all have dedicated keywords

Filtering Operators

  • Where operators --> returns bool
  • test each element of each input sequence.
  • OfType eg:
.OfType<string>

or

.OfType<int>

Sorting Operators

  • OrderBy
  • ThenBy
  • Reverse (reverse the order of elements)

Set Operators

  • Distinct - removes duplicate values
  • Except - diff of two sequences
  • Intersect - intersection of 2 sequences
  • **Union
  • eg:
int[] twos = { 2, 4, 6, 8, 10 }
int[] threes = { 3, 6, 9, 12, 15}; 
  • result = 6
var intersection = twos.Intersect(threes);

// 2, 4, 8, 10
// except = twos.Except(threes);

// 2, 4, 6, 8, 10, 3, 9, 12, 15
var union = twos.Union(threes);

Quantifiers

  • .All
  • .Any
  • .Contains

Projection Operators

  • Select - every one that it gets, it spits out 1
  • SelectMany - flattens out projects across multiple sequences
  • if you are using the comprehension syntax and you have a second from keyword in your query then you are automatically using SelectMany

Paritioning Operators

Skip / SkipWhile - skip elements until a condition or predicate is met
Take / TakeWhile - take elements until a condition or predicate is met

eg:
// yields 5, 7
var query = numbers.Skip(2).Take(2);

// yileds 5, 7, 9
var query = numbers.SkipWhile(n => n < 5) // skip until I get to a result that is less
// than 5
.TakeWhile(n => n < 10);

Joining

  • Join - Join two sequences on a key and yields a sequence (flat result)
  • GroupJoin - Join two sequences on a key and yields groups of sequences (hierarchical result)
  • when using the comprehension syntax, you must use the "equals" keyword

Grouping

  • GroupBy - group elements from a sequence - lazy operator - execution is deferred
  • ToLookup - Infer elements into a one to many dictionary - greedy operator will execute immediately
  • The only difference between these two is that the GroupBy is lazy and the ToLookup is greedy
  • Any operators that start with a "to" are always going to be greedy

Generation Operators

  • Empty - returns an empty collection
  • Range - Generates a sequence of numbers
  • Repeat - Generates a collection of repeated values
  • DefaultIfEmpty - Replaces empty collection with collection of 1 default value for it's type

Equality

  • SequenceEqual - Compares elements in two sequences

Element

  • ElementAt - Returns the element at a specific index
  • First - Returns the first element of a collection
  • Last - Returns the last element of a collection
  • Single - Returns a single element
  • OrDefault ... will return the default value for the type you are querying if it doesn't exist in the sequence

Conversion

  • AsEnumerable - Returns input as IEnumberable - will make something a local in-memory process
  • AsQueryable - Converts IEnumerable to IQueryable - allows you to simulate LINQ to SQL - eg use them in unit test projects where we are trying to similate a linqtosql provider
  • Cast - Coerce all elements to a type - forces all of the elements to a specific type - if an element isn't of that type, then the whole thing will fail
  • OfType - filters values that can be coerced to a type
  • ToArray - Converts sequence to an array (immediate) - forces execution
  • ToDictionary - Convert sequence to Dictionary<K,V>
  • ToList - Convert sequence to List
  • ToLookup - Group elements into an IGrouping<K,V>

Sample code for toDictionary:

	Dictionary<int, Employee> employeeDictionary =
		employee.ToDictionary(e => e.ID,	// key selector
							  e => e);		// value selector

Concatendation

  • Concat - Concatenates two sequences into a single sequence
  • difference between a concat and a union: union will also remove any duplicates, where as concat will not.

Aggregration

  • Aggregate
  • Average - average value of in a sequence
  • Count
  • Max - max value in a sequence
  • Min - min value in a sequence
  • Sum - calculates the sum of the values in a sequence

Aggregate Method: takes 3 inputs:

  1. Seed = initialize - initialize some accumulator. Since, in this example, I am aggregating strings, A good class to aggregate strings is a stringBuilder
  2. Accumulator Function - Aggregate
  3. Result Selector - Terminate

Program.cs

private static void Aggregation()
        {
            Employee employee = new Employee { ID = 2 };

            var rules = new List<Rule<Employee>>()
            {
                new Rule<Employee> { Test= e => !String.IsNullOrEmpty(e.Name), 
                                     Message= "Employee name cannot be empty" },

                new Rule<Employee> { Test = e => e.DepartmentID > 0, 
                                     Message = "Employee must have an assigned department"},

                new Rule<Employee> { Test = e => e.ID > 0, 
                                     Message = "Employee must have an ID"}
            };


            bool isValid = rules.All(r => r.Test(employee));

            if (!isValid)
            {
                var failedRules = rules.Where(r => r.Test(employee) == false);

                string errorMessage =
                            failedRules.Aggregate(new StringBuilder(), 
                                        (sb, r) => sb.AppendLine(r.Message), 
                                        sb => sb.ToString());
            }

        }

Rule.cs

public class Rule<T>
   {
       public Func<T, bool> Test { get; set; }
       public string Message { get; set; }        
   }
  • LINQ is functionally pure - don't have any side effects. they don't modify the input.