Basic Concepts About LINQ In C#

In this article, I am explaining about LINQ in C#. Basically you can write Linq in C# in two ways,
  • Standard syntax which is like calling methods (calling standard LINQ operators as method) directly.
  • Query expression syntax which is similar to SQL queries. 
For example,
Let’s assume we have 100 numbers in an array and we want to get the result of only odd numbers, so we can use the above two syntaxes as follows,
  • Using standard syntax
    1. var res = listOfNumbers.Where(n => n % 2 != 0);  
  • Using query expression syntax: 
    1. var res = from n in listOfNumbers where n%2!=0 select n ;  
Both these syntax will give the same result, you can choose any syntax based on your comfort level. As you can see the query expression syntax looks similar to SQL statements, but the clause sequence of these two syntaxes are different as follows: 
In SQL Query syntax keyword sequence: 
In Query expression syntax keyword sequence: 
from->[variable name] in [source]->where->groupby->orderby->select 
Here you can see that the query expression keywords are all in lower case, written in one-word and there is no having keyword in LINQ expression. Linq uses variables to iterate the source or store the middle result.
With a standard LINQ query we can do a lot of collection data operations, including sorting, filtering, transforming, and advanced operations such as joining and grouping.
The result value of LINQ queries can be a collection or a single value. LINQ Query expressions have operators that are extension methods or static methods, such as Where, OrderBy, Select, GroupBy, Join, etc.
  1. Always use var to declare the variable which references the LINQ operation result. That’s because sometimes the type LINQ operation result is too complicated and/or too long to spell. 
  2. Avoid writing overly long LINQ queries. Keep the LINQ queries short and clear. This helps with readability. 
Where Operator
It accepts a Func<TSource, bool> type parameter, which is the filtering condition. When doing a filtering operation, the Where operator will iterate the source collection and apply this condition to each of the elements, and the elements in the source collection will be passed into the Func<TSource, bool> type delegate one by one. If an element makes the Func<TSource, bool> delegate return true, then that element satisfies the conditions and will be added to the result. 
ofType Operator
The OfType operator can help us pick out the instances of a specific child class type.
For example,
  1. var objects = new Object[] { 8, 6L, 1.54, "Hello", 1, 3 };  
  2. var result = objects.OfType<int>();  
  3. foreach (var item in result) {  
  4.     System.Console.WriteLine(item);  
  5. }  
The code var result = objects.OfType<int>(); helps to select only all the int type data, here output will be - 8,1,3. 
All Operator
It determines whether all the elements in a collection satisfy a condition. It accepts a Func<TSource, bool> type parameter as the condition. The All operator will iterate the collection and pass each of the elements into the Func<TSource, bool> type delegate to get true or false. If all returned values of the delegate are true, that means all of the collection elements satisfy the condition, the All query will return true. If any element produces a false, the All query will return false.
For example,
  1. var res = StudentRecords.All(s => s.Age > 18);  
  2. System.Console.WriteLine(res);  
The above code will check in StudentRecords, for those above age 18;  if it's yes then it returns true otherwise it returns false.
Contains Operator
It determines whether a collection contains a specified element and it returns true or false.
For example,
  1. string[] names = { “Alex”,”Priya”,"Suresh""Binnu""Rafnas""Meenakshi" };  
  2. var res = names.Contains(“Rafnas”);  
  3. System.Console.WriteLine(res);//OutPut: True  
If you want to check whether the object of the record  exists or not:
  1. var employee = new Employee("Rafnas", “EMP1568”,”IT”);  
  2. var res = employee.Contains(employee);  
  3. System.Console.WriteLine(res);  
For that we need to use a comparer to compare the employee object and call another overload version of Contains Operator with the declared comparer.
  1. public static bool Contains<TSource> (this System.Collections.Generic.IEnumerable<TSource> source, TSource value, System.Collections.Generic.IEqualityComparer<TSource> comparer);  
Here comparer parameter is IEqualityComparer<TSource>, so we need to declare a class to implement this interface, and the TScource should be replaced by Employee(class name) as follows,
  1. class EmployeeComparer : IEqualityComparer<Employee> {  
  2.     public bool Equals(Employee x, Employee y) {  
  3.         return x.Name == y.Name && x.Gender == y.Gender && x.Rank == y.Rank;  
  4.     }  
Then we can modify our filter query as below,
  1. var employee = new Employee("Rafnas", “EMP1568”,”IT”);  
  2. var res = employee.Contains(employee,new EmployeeComparer());  
  3. System.Console.WriteLine(res);  
Now the Contains Operator will compare the equality of the employee records using our customized comparison logic.
Distinct Operator
It is used to keep unique elements of the collection and removes duplicate elements.
For example,
  1.    string[] names = { "Suresh""Binnu""Rafnas",”Binnu”, "Meenakshi",”Rafnas” };  
  2. var distinctResult = names.Distinct();  
  3. Console.WriteLine(string.Join(",", distinctResult));   
  5. /OutPut: Suresh, Binnu, Rafnas, Meenakshi  
Intersect Operator
It is used to return the set intersection; that means elements appear in both collections.
For example,
  1.     string[] set1 = { "Suresh""Rafnas", ”Alex”, "Suresh", };  
  2.     string[] set2 = { "Ahalya""Rafnas""Meenakshi", ” Alex” };  
  4.  var intersectResult = set1.Intersect(set2);  
  5.  Console.WriteLine(string.Join(",",intersectResult));   
  7. //OutPut: Rafnas, Alex  
Except Operator
It is used to return the set difference; that means elements of one collection that do not appear in a second collection.
For example,
  1.     string[] set1 = { "Suresh""Rafnas", ”Alex”, "Suresh", };  
  2.     string[] set2 = { "Ahalya""Rafnas""Meenakshi", ” Alex” };  
  4.  var exceptResult = set1.Except(set2);  
  5.  Console.WriteLine(string.Join(",",exceptResult));   
  7. //OutPut: Suresh, Ahalya, Meenakshi  
Union Operator
It is used to return the set union; that means unique elements that appear in either of two collections.
For example,
  1.     string[] set1 = { "Suresh""Rafnas", ”Alex”, "Suresh", };  
  2.     string[] set2 = { "Ahalya""Rafnas""Meenakshi", ” Alex” };  
  4.  var unionResult = set1.Union(set2);  
  5.  Console.WriteLine(string.Join(",",unionResult));   
  7. //OutPut: Suresh, Rafnas, Alex, Ahalya, Meenakshi  
Concatenation Operator
It is used to return all elements from two collections with duplicate elements.
For example,
  1.     string[] set1 = { "Suresh""Rafnas", ”Alex”, "Suresh", };  
  2.     string[] set2 = { "Ahalya""Rafnas""Meenakshi", ” Alex” };  
  4.  var concatResult = set1.Concat(set2);  
  5.  Console.WriteLine(string.Join(",",concatResult));   
  7. //OutPut: Suresh, Rafnas, Alex, Suresh, Ahalya, Rafnas, Meenakshi, Alex  
Sorting Operators
These operators are used to order the elements of a sequence based on one or more attributes.
  1. OrderBy - it is used to order the elements in ascending order
  2. OrderByDescending - it is used to order the elements in descending order
  3. ThenBy - it is used to perform secondary sort in ascending order
  4. ThenByDescending - it is used to perform secondary sort in descending order
  5. Reverse - it is used to reverse the order of elements in a collection.
All of the above five operators can provide an extra IComapare<TKey> type parameter to override the default sorting method.