LINQ (Language Integrated Query) is uniform query syntax in C# and VB.NET to retrieve data from different sources and formats.
SQL is a Structured Query Language used to save and retrieve data from a database. In the same way, LINQ is a structured query syntax built in C# and VB.NET to retrieve data from different types of data sources such as collections, ADO.Net DataSet, XML Docs, web service and MS SQL Server and other databases.
LINQ queries return results as objects. It enables you to uses object-oriented approach on the result set and not to worry about transforming diffent formats of results into objects.
The following example shows the complete query operation. The complete operation includes creating a data source, defining the query expression, and executing the query in a foreach
statement.
class LINQQueryExpressions {
static void Main() {
// Specify the data source.
int[] scores = new int[] { 97, 92, 81, 60 };
// Define the query expression.
IEnumerable<int> scoreQuery = from score in scores
where score > 80 select score;
// Execute the query.
foreach (int i in scoreQuery) {
Console.Write(i + " ");
}
}
}
// Output: 97 92 81
There are two basic ways to write a LINQ query to IEnumerable collection or IQueryable data sources.
Query Syntax:
Query syntax is similar to SQL (Structured Query Language) for the database. It is defined within the C# or VB code.
from <range variable> in <IEnumerable<T> or IQueryable<T> Collection>
<Standard Query Operators> <lambda expression>
<select or groupBy operator> <result formation>
The LINQ query syntax starts with from keyword and ends with select keyword.
// string collection
IList<string> list = new List<string>() {
"Item 1",
"Item 2",
"Item 3",
"Item 4" ,
"Item 5"
};
// LINQ Query Syntax
var result = from s in list
where s.Contains("5")
select s;
LINQ Method Syntax:
Method syntax uses extension methods included in the Enumerable or Queryable static class, similar to how you would call the extension method of any class.
The following is a sample LINQ method syntax query that returns a collection of strings which contains a word "Item 5".
// string collection
IList<string> list = new List<string>() {
"Item 1",
"Item 2",
"Item 3",
"Item 4" ,
"Item 5"
};
// LINQ Query Syntax
var result = list.Where(s => s.Contains("Item 5"));
The method syntax comprises of extension methods and Lambda expression. The extension method Where() is defined in the Enumerable class.
Lambda Expression:
C# 3.0(.NET 3.5) introduced the lambda expression along with LINQ. The lambda expression is a shorter way of representing anonymous method using some special syntax.
For example, following anonymous method checks if person is adult or not:
delegate(Person person) { return person.Age > 30; };
The above anonymous method can be represented using a Lambda Expression in C#
person => person.Age > 30
You can pass the lambda expression assigned to the Func delegate to the Where() extension method in the method syntax as shown below:
IList<Person> Persons = new List<Person>(){...};
Func<Person, bool> isPersonAdult = p => p.age > 30;
var adultPersons = Persons.Where(isPersonAdult).ToList<Student>();
Standard Query Operators in LINQ are actually extension methods for the IEnumerable<T> and IQueryable<T>
types. They are defined in the System.Linq.Enumerable
and System.Linq.Queryable
classes. There are over 50 standard query operators available in LINQ that provide different functionalities like filtering, sorting, grouping, aggregation, concatenation, etc.
Where:
The Where operator filters the collection based on a given criteria expression and returns a new collection. The criteria can be specified as lambda expression or Func delegate type.
The Where extension method has following two overloads. Both overload methods accepts a Func delegate type parameter.
This query uses a Where operator to filter the Person
who is adult from the given collection. It uses a lambda expression as a predicate function.
var result = from s in Persons
where person.Age > 30 && person.Age < 80
select person.PersonName;
You can also use a Func type delegate with an anonymous method to pass as a function as below :
Func<Person,bool> isAdult = delegate(Person person) {
return person.Age > 30 && person.Age < 80;
};
var result = from person in Persons
where isAdult(person)
select person;
You need to pass whole lambda expression as a predicate function instead of just body expression in LINQ method syntax.
var result = Persons.Where(person => person.Age > 30 && person.Age < 80);
OrderBy:
OrderBy sorts the items of a collection in ascending or descending order. It sorts the collection in ascending order by default. Use descending keyword to sort collection in descending order.
var result = from person in Persons
orderby person.PersonName descending
select person;
The following example sorts the Persons
collection in ascending order of PersonName
using OrderBy method.
var result = Persons.OrderBy(person => person.PersonName);
And OrderByDescending sorts the collection in descending order.
var result = Persons.OrderByDescending(person => person.PersonName);
ThenBy:
Use ThenBy() method after OrderBy to sort the collection on another field in ascending order.
var result = Persons.OrderBy(person => person.Age).ThenBy(person => person.PersonName);
or use ThenByDescending method to apply secondary sorting in descending order
var result = Persons.OrderBy(person => person.Age).ThenByDescending(person => person.PersonName);
GroupBy:
The GroupBy operator returns groups of elements based on some key value. Each group is represented by IGrouping<TKey, TElement> object.
The following example creates a groups of persons who have same age. Persons of the same age will be in the same collection and each grouped collection will have a key and inner collection, where the key will be the age and the inner collection will include students whose age is matched with a key.
var result = from person in Persons
group person by person.Age;
//iterate each group
foreach (var persons in result)
{
//Each group has a key
Console.WriteLine("Age Group: {0}", persons.Key);
// Each group has inner collection
foreach(Person person in persons)
Console.WriteLine("Person Name: {0}", person.PersonName);
}
The GroupBy() extension method works the same way in the method syntax.
var result = Persons.GroupBy(person => person.Age);
ToLookup:
ToLookup is the same as GroupBy, the only difference that ToLookup execution is immediate. Also, ToLookup is only applicable in Method syntax.
var result = Persons.ToLookup(person => person.age);
Join:
The Join operator joins two collections based on a key and returns a resulted sequence.
The following example joins two string collection and return new collection that includes matching strings in both the collection.
var result = Persons.Join(// outer sequence
Users, // inner sequence
person => person.PersonID, // outerKeySelector
user => user.UserID, // innerKeySelector
(person, user) => new // result selector
{
PersonName = person.PersonName,
UserName = user.UserName
});
The following example of Join operator in query syntax returns a collection of elements from Persons and Users if their person.PersonID
and user.UserID
is match.
var result = from person in Persons // outer sequence
join user in Users //inner sequence
on person.PersonID equals user.UserID // key selector
select new { // result selector
PersonName = person.PersonName,
UserName = user.UserName
};
GroupJoin:
The GroupJoin operator joins two sequences based on key and groups the result by matching key and then returns the collection of grouped result and key.
In this example of GroupJoin query, Users
is the outer sequence, because the query starts from it. The first parameter in GroupJoin method is to specify the inner sequence, which is Persons
. The second and third parameters of the GroupJoin() method are to specify a field whose value should be matched using lambda expression, in order to include element in the result. The key selector for the outer sequence user => user.UserID
indicates that UserID field of each elements in Users should be match with the key of inner sequence Persons person => person.PersonID
. If value of both the key field is matched then include that element into grouped collection personsGroup
where key would be UserID
.
The last parameter in Join method is an expression to formulate the result. In the above example, result selector includes grouped collection personsGroup
and UserName
.
var result = Users.GroupJoin(Persons, //inner sequence
user => user.UserID, //outerKeySelector
person => person.PersonID, //innerKeySelector
(user, personsGroup) => new // resultSelector
{
persons = personsGroup,
UserFulldName = user.UserName
});
foreach (var item in result)
{
Console.WriteLine(item.UserFulldName);
foreach(var person in item.persons)
Console.WriteLine(person.PersonName);
}
The following example demonstrates the GroupJoin in query syntax.
var result = from user in Users
join person in Persons
on user.UserID equals person.PersonID
into personGroup
select new {
persons = personGroup ,
UserName = user.UserName
};
Select:
The Select operator always returns an IEnumerable collection which contains elements based on a transformation function.
The following example demonstrates select operator that returns a string collection of PersonName.
var result = from person in Persons
select person.PersonName;
The following example of the select clause returns a collection of anonymous type containing the Name and Email property.
var result = from person in Persons
select new { Name = person.PersonName, Email = person.PersonEmail };
In the following example, Select extension method returns a collection of anonymous object with the Name and Email property:
var result = Persons.Select(person => new { Name = person.PersonName ,
Email = perspn.Email });
All:
The All operator evalutes each elements in the given collection on a specified condition and returns True if all the elements satisfy a condition.
bool areAllPersonsAdult = Persons.All(person => person.Age > 30);
Any :
Any checks whether any element satisfy given condition.
bool isAnyPersonAdult = Persons.Any(person => person.Age > 30);
Contains:
The Contains operator checks whether a specified element exists in the collection or not.
IList<int> list = new List<int>() { 1, 2, 3, 4, 5 };
bool result = list.Contains(20);
To compare values of the person object, you need to create a class by implementing IEqualityComparer interface, that compares values of two Persons objects and returns boolean.
The following is a PersonComparer class that implements IEqualityComparer<Person> interface to compare values of two Persons objects:
class PersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person p, Person p1)
{
if (p.PersonID == p1.PersonID &&
p.PersonName.ToLower() == p1.PersonName.ToLower())
return true;
return false;
}
public int GetHashCode(Person p)
{
return p.GetHashCode();
}
}
Now, you can use the above PersonComparer class in the overload method of Contains extension method :
Person person = new Person(){ PersonID = 1, PersonName = "Marwen"};
bool result = Persons.Contains(peron, new PersonComparer());
Aggregation:
Performs a custom aggregation operation on the values in the collection.
IList<String> list = new List<String>() { "A", "B", "C", "D", "E"};
var result = list.Aggregate((str, str1) => str + " | " + str1);
// A | B | C | D | E
The following example uses string as a seed value in the Aggregate extension method.
string reslut = Persons.Aggregate<Person, string>(
"Ages : ", // seed value
(str, s) => str += person.PersonAge + " | " );
The following example use Aggregate operator to sum the age of all the persons.
int age = Persons.Aggregate<Person, int>(0,(total, person) => total += person.PersonAge );
Average:
Average extension method calculates the average of the numeric items in the collection.
var average = Persons.Average(person => person.PersonAge);
Count :
The Count operator returns the number of elements that have satisfied the given condition.
var total = Persons.Count();
Console.WriteLine("Total Persons: {0}", total);
var adults = Persons.Count(person => person.PersonAge > 30);
Console.WriteLine("Number of Adult Persons: {0}", adults );
ElementAt:
The ElementAt method returns an element from the specified index from a given collection. If the specified index is out of the range of a collection then it will throw an Index out of range exception.
IList<int> list = new List<int>() { 1, 2, 3, 4, 5, 6 };
var result = list.ElementAt(0);
The ElementAtOrDefault method returns an element from the specified index from a collaction and if the specified index is out of range of a collection then it will return a default value of the data type instead of throwing an error.
var result = list.ElementAtOrDefault(0);
First:
The First method returns the first element of a collection, or the first element that satisfies the specified condition using lambda expression or Func delegate.
var result = list.First(i => i % 2 == 0);
FirstOrDefault:
The FirstOrDefault method returns default value of the data type of a collection if a collection is empty or doesn't find any element that satisfies the condition.
var result = list.FirstOrDefault();
Last:
The Last method returns the last element from a collection, or the last element that satisfies the specified condition using lambda expression or Func delegate.
var result = list.Last(i => i % 2 == 0);
LastOrDefault:
The LastOrDefault method does the same thing as Last() method. The only difference is that it returns default value of the data type of a collection if a collection is empty or doesn't find any element that satisfies the condition.
var result = list.LastOrDefault();
Single :
The Single method returns the only element from a collection, or the only element that satisfies the specified condition.
var reslut = list.Single(i => i == 5);
The SingleOrDefault method does the same thing as Single() method. The only difference is that it returns default value of the data type of a collection if a collection is empty.
var result = list.SingleOrDefault(i => i == 5);
SequenceEqual:
The SequenceEqual method checks whether the number of elements, value of each element and order of elements in two collections are equal or not.
bool result = list1.SequenceEqual(list2);
To compare the values of two collection of complex type, you need to implement IEqualityComperar<T> interface.
bool result = Persons1.SequenceEqual(Person2, PersonComparer());
Concat:
The Concat method appends two collections of the same type and returns a new collection.
var Persons3 = Persons1.Concat(Persons2);
Empty:
The Empty method is a static method included in Enumerable static class. You can call it the same way as other static methods.
var Persons = Enumerable.Empty<Person>();
Range:
The Range method returns a collection of IEnumerable<T> type with specified number of elements and sequential values starting from the first element.
var result = Enumerable.Range(1, 10);
Repeat:
The Repeat method generates a collection of IEnumerable<T> type with specified number of elements and each element contains same specified value.
var result = Enumerable.Repeat<int>(1, 10);
Distinct:
The Distinct extension method returns a new collection of unique elements from the given collection.
IList<int> list = new List<int>(){ 1, 1, 3, 2, 4, 4, 3, 5 };
var result = intList.Distinct();
The Distinct extension method doesn't compare values of complex type objects. You need to implement IEqualityComparer<T>
interface in order to compare the values of complex types.
var result = Persons.Distinct(new PersonComparer());
Except:
The Except method requires two collections. It returns a new collection with elements from the first collection which do not exist in the second collection.
IList<string> list1 = new List<string>(){"1", "2", "3", "4", "5" };
IList<string> list2 = new List<string>(){"3", "4", "5", "6", "7"};
var result = list1.Except(list2);
You need to implement IEqualityComparer interface in order to get the correct result from Except method.
var result = Persons1.Except(Persons2,new PersonComparer());
Intersect:
The Intersect extension method requires two collections. It returns a new collection that includes common elements that exists in both the collection.
IList<string> list1 = new List<string>() { "1", "2", "3", "4", "5" };
IList<string> list2 = new List<string>() { "3", "4", "5", "6", "7"};
var result = list1.Intersect(list2);
The Intersect extension method doesn't return the correct result for the collection of complex types. You need to implement IEqualityComparer interface in order to get the correct result from Intersect method.
var result = Persons1.Intersect(Persons2, new PersonComparer());
Union:
The Union extension method requires two collections and returns a new collection that includes distinct elements from both the collections.
IList<string> list1 = new List<string>() { "1", "2", "3", "4", "5" };
IList<string> list2 = new List<string>() { "3", "4", "5", "6", "7"};
var result = list1.Union(list2);
The Union extension method doesn't return the correct result for the collection of complex types. You need to implement IEqualityComparer interface in order to get the correct result from Union method.
var result = Persons1.Union(Persons2, new PersonComparer());
Skip:
The Skip method skips the specified number of element starting from first element and returns rest of the elements.
IList<string> list = new List<string>() { "1", "2", "3", "4", "5" };
var result = list.
Skip(3);
SkipWhile :
the SkipWhile extension method in LINQ skip elements in the collection till the specified condition is true. It returns a new collection that includes all the remaining elements once the specified condition becomes false for any element.
var result = list.
SkipWhile(item => item.Length < 3);
Take:
The Take extension method returns the specified number of elements starting from the first element.
var result = list.
Take(3);
TakeWhile:
The TakeWhile extension method returns elements from the given collection until the specified condition is true.
var result = list.
TakeWhile(item => item.Length < 3);
AsEnumerable:
The AsEnumerable method cast a source object to IEnumerable<T> .
var result = Persons.AsEnumerable();
The AsQueryable method convert a source object to IQueryable<T>.
var result = Persons.
AsQueryable();
Cast:
Cast does the same thing as AsEnumerable<T>. It cast the source object into IEnumerable<T>.
var result = Persons.Cast<Person>();
ToArray:
ToArray convert a source object into an array.
Person[] PersonArray = Persons.ToArray<Person>();
ToList:
ToList convert a source object into List.
IList<Person> list = PersonArray.ToList<Person>();
ToDictionary - Converts a Generic list to a generic dictionary.
IDictionary<Person,int>Person
Dictionary=Persons.ToDictionary<Person,int>(person => person.PersonID);