LINQ stands for Language Integrated Query.
It is a .Net query language used against strongly-typed objects.
To use LINQ in C#:
using System.Linq;
LINQ queries are strongly-typed.
LINQ to Objects let you run queries against IEnumerables. It is a declarative language that is easier to read/write than the nested foreach loops it replaces.
LINQ builds up a query, and does not actually execute it until you run an operation that requires it.
using System.Linq;
var list = from x in fullList where x.Property < 5 select x;
//query has not been run yet
int count = list.Count();
//query was run so that count could be performed
Basic example:
var query = from x in _db.Table
where x.Property < 5
orderby x.Name ascending
select x;
Projection example:
var query = from x in _db.Table
where x.Property < 5
orderby x.Name ascending
select new Result() {
Id = x.Id,
Name = x.Name,
CountInstances = x.Instances.Count()
};
Method syntax makes use of Extension Methods and Lambda Expression.
Method syntax is also called Fluent syntax.
var query = _db.Table
.Where(x => x.Property < 5)
.OrderBy(x => x.Name)
.Take(10);
The method syntax uses Method Chaining to combine as many query clauses as you need.
List<Part> parts = new List<Part>() { partA, partB... };
IQueryable<Part> list = parts.Skip(10);
list = list.Take(4).OrderBy(x => x.PartNumber);
Method Chaining means that each method returns the object the method is a part of, allowing you to immediately call another method on that object.
public class MethodChaining
{
private int sum = 0;
public MethodChaining Add(int x)
{
sum += x;
return this;
}
public MethodChaining Substract(int x)
{
sum -= x;
return this;
}
}
...
MethodChaining methodChaining = new MethodChaining();
methodChaining.Add(5).Add(3).Subtract(4).Add(9);
You can use Extension Methods to create your own custom LINQ clauses.
public static class LINQExtensions
{
public static IEnumerable<T> Alternates(this IEnumerable<T> query)
{
List<T> alternates = new List<T>();
for(int i=0; i<query.Count(); i+=2)
{
alternates.Add(query.ElementAt(i));
}
return alternates;
}
}
public static class LINQExtensions
{
public static double Median(this IEnumerable<double> query)
{
int count = query.Count();
if(count == 0) throw new InvalidOperationException("Set is empty");
query = query.OrderBy(x => x);
int midIndex = (int)(count / 2);
if(count % 2 == 0)
{
return (query.ElementAt(midIndex) + query.ElementAt(midIndex-1)) / 2;
}
else
{
return query.ElementAt(midIndex);
}
}
}
List<Part> parts = new List<Part>() { partA, partB... };
var subList = from x in parts select x;
Part[] subList = (from x in parts select x).ToArray();
var subList = from x in parts where x.Name.StartsWith("R") select x;
OrderBy
var subList = from x in parts orderby x.PartNumber ascending select x;
Select
var subList = from x in parts select new { Number = x.PartNumber, WeightLbs = x.Ounces/16 };
Group
var byLastName =
from s in students
group s by s.LastName into newGroup
orderby newGroup.Key
select newGroup;
foreach(var nameGroup in byLastName)
{
Console.WriteLine("Group key = {0}", nameGroup.Key);
foreach(var student in nameGroup)
{
Console.WriteLine("Group member = {0}", student.Name);
}
}
Returns the first element. Throws an exception if the enumerable if empty. Optional lambda.
Student student = students.First();
Student student = students.First(x => x.LastName == "Roberts");
Returns the first element. Returns the default type value if the enumerable is empty. Optional lambda.
(For reference types, NULL is default. For value types, 0 is default.)
Student student = students.FirstOrDefault();
Student student = students.FirstOrDefault(x => x.LastName == "Roberts");
Returns the last element. Throws an exception if the enumerable is empty. Optional lambda.
Student student = students.Last();
Student student = students.Last(x => x.LastName == "Roberts");
Returns the last element. Returns the default type value if the enumerable is empty. Optional lambda.
(For reference types, NULL is default. For value types, 0 is default.)
Student student = students.LastOrDefault();
Student student = students.LastOrDefault(x => x.LastName == "Roberts");
Returns the only element in the enumerable. Throws an exception if the enumerable length is not exactly 1. Optional lambda.
Student student = students.Single();
Student student = students.Single(x => x.LastName == "Roberts");
Keep the next N elements
IEnumerable<Student> students = students.Take(4);
Keep all elements until the condition is false:
IEnumerable<Student> students = students.TakeWhile(x => x.Age >= 21);
Skip the next N elements
IEnumerable<Student> students = students.Skip(4);
Skip all elements until the condition is false
IEnumerable<Student> students = students.SkipWhile(x => x.Age >= 21);
Sets what type to return from the query.
IEnumerable<int> studentAges = students.Select(x => x.Age);
MyType[] results = students.Select(x => new MyType() {
Name = x.Name,
Age = x.Age
}).ToArray();
You can return anonymous types.
var studentSummaries = students.Select(x =>
new { LastName = x.LastName, BestGrade = x.Grades.Max() });
Flattened nested data.
Language[] languages = books.SelectMany(b => b.Titles).Select(t => t.Language).ToArray();
Return just distinct elements.
IEnumerable<string> surNames = students.Select(x => x.LastName).Distinct();
Groups the elements by the selected property.
Returns an enumerable of objects. Each has a Key and is also an enumerable you can iterate over to see the elements in the group.
IEnumerable<IGrouping<string, Student>> studentGroups = students.GroupBy(x => x.LastName);
foreach(IGrouping<string, Student> group in studentGroups)
{
Console.WriteLine("Group key = {0}", group.Key); //Key is the LastName
foreach(Student student in group)
{
Console.WriteLine("Group member = {0}", student.FirstName);
}
}
You can supply a custom comparer in GroupBy. Each pair of elements will be checked for equality with the custom comparer.
using System;
using System.Collections.Generic;
using System.Linq;
public class CustomComparer : IEqualityComparer<T>
{
public bool Equals(T a, T b)
{
//return true if a and b should be considered equal
}
public int GetHashCode(T obj)
{
//return hashcode
}
}
...myList.GroupBy(x => x, new CustomComparer());
Sort the elements by the specified value. Ascending order.
IEnumerable<Student> students = students.OrderBy(x => x.LastName);
Use a custom comparison function:
using System.Collections.Generic;
public class MyComparer : IComparer<MyType>
{
public int Compare(MyType a, MyType b)
{
//return positive if "a" should be higher, return negative if "b" should be higher
}
}
//to use it
var result = collection.OrderBy(x => x, new MyComparer())
Sort the elements by the specified value. Descending order.
IEnumerable<Student> students = students.OrderByDescending(x => x.LastName);
This will set the next level of sorting. Use after an OrderBy, OrderByDescending, ThenBy, or ThenByDescending clause. Ascending order.
IEnumerable<Student> students = students.OrderBy(x => x.LastName).ThenBy(x => x.FirstName);
This will set the next level of sorting. Use after an OrderBy, OrderByDescending, ThenBy, or ThenByDescending clause. Descending order.
IEnumerable<Student> students = students.OrderBy(x => x.LastName).ThenByDescending(x => x.Age);
Filters elements by a conditional statement
IEnumerable<Student> students = students.Where(x => x.LastName == "Roberts");
Filters elements by their Type
IEnumerable<Student> students = people.OfType<Student>();
Given two enumerables, returns the distinct elements of both enumerables together.
IEnumerable<int> luckyOrPrime = primeNumbers.Union(luckyNumbers);
You can also define what comparison function to use.
IEnumerable<int> luckyOrPrime = primeNumbers.Union(luckyNumbers, new IEqualityComparer<T>());
Return the element count
int count = numbers.Count();
Return the average of the values.
decimal averageNumber = numbers.Average();
decimal averageAge = students.Average(x => x.Age);
Returns the summation of the values.
int sumNumbers = numbers.Sum();
int sumAges = students.Sum(x => x.Age);
Returns true if any element of the enumerable matches the condition:
bool includesAdults = students.Any(s => s.Age >= 18);
Explicitly join objects, like a SQl table join.
var result = dbContext.TableA.Join(
dbContext.TableB,
outer => outer.TableB.Column,
inner => inner.TableA.Column,
(outer, inner) => new { A = outer, B = inner}
);
Build a dictionary:
Dictionary<int, name> dict = list.ToDictionary(x => x.Id, x => x.Name);
(Method Syntax is also called Fluent Syntax)
LINQ to Entities includes all the LINQ to Objects Method Syntax options.
using (var context = new MyEntities())
{
return context.Students.Where(x => x.LastName == "Roberts").FirstOrDefault();
}
Explicitly loading data when using lazy loading.
Student student = context.Students.Include("Classes").Include("History.PreviousSchools").FirstOrDefault();
Filtering by a subtype.
Student student = context.People.OfType<Student>().FirstOrDefault();
LINQ to Entities includes all the LINQ to Objects Syntax Syntax options.
using (var context = new MyEntities())
{
return (from student in context.Students where student.LastName == "Roberts" select student).First();
}
You can also query the entities with a SQL-like language.
string queryString = "SELECT VALUE s FROM MyEntities.Students AS s WHERE s.LastName == 'Roberts'";
var objectContext = (context as IObjectContextAdapter).ObjectContext;
ObjectQuery<Student> students = objectContext.CreateQuery<Student>(queryString);
using (var context = new MyEntities())
{
return context.Students.SqlQuery("SELECT Id, FirstName FROM Student WHERE LastName == 'Roberts'");
}
todo
todo