Linq Notes
Linq Notes
1|Page
Part 32 - LinqPad Tutorial..............................................................................................................91
Part 33 - LINQ to SQL....................................................................................................................99
Part 34 - Insert Update Delete using LINQ to SQL..................................................................103
Part 35 - How to view LINQ to SQL generated SQL queries..................................................106
Part 36 - Using stored procedures with LINQ to SQL..............................................................107
Part 37 - Insert Update Delete using stored procedures in LINQ to SQL.............................109
Part 38 - Stored procedures with output parameters in LINQ to SQL...................................111
Part 39 - What is SqlMetal...........................................................................................................113
Part 40 - Lazy loading in LINQ to SQL.......................................................................................115
Part 41 - Eager loading in LINQ to SQL....................................................................................119
Part 42 - Difference between eager loading and lazy loading................................................121
Part 43 - Single table inheritance in linq to sql..........................................................................122
Part 44 - Single table inheritance - saving to database...........................................................127
Part 45 - Compiled queries in Linq to SQL................................................................................128
Part 46 - How to directly execute sql queries using Linq to SQL...........................................130
Part 47 - Identity Cache in Linq to SQL.....................................................................................131
Part 48 - Difference between optimistic and pessimistic concurrency control......................137
Part 49 - Concurrency in Linq to SQL........................................................................................139
Part 50 - Handling ChangeConflictException............................................................................142
Part 51 - UpdateCheck property.................................................................................................144
Part 52 - Using ROWVERSION or TIMESTAMP to detect concurrency conflicts................146
Part 53 - LINQ to XML..................................................................................................................148
Part 54 - Creating an XML document using in-memory collection of objects.......................151
Part 55 - Querying xml document using linq to xml..................................................................154
Part 56 - Modifying xml document using linq to xml.................................................................155
Part 57 - Transforming XML to CSV using LINQ to XML........................................................157
Part 58 - Transforming XML to HTML table using LINQ to XML............................................158
Part 59 - Transform one XML format to another XML format using linq to xml....................160
Part 60 - XML validation against XSD........................................................................................161
2|Page
Part 1 - What is LINQ
In this video, we will discuss
1. What is LINQ
2. Why should we use LINQ and what are the benefits of using LINQ
3. LINQ Architecture
4. LINQ Providers
5. LINQ to SQL Example and LINQ to Objects example
What is LINQ
LINQ stands for Language Integrated Query. LINQ enables us to query any type of data store
(SQL Server, XML documents, Objects in memory etc).
Why should we use LINQ and what are the benefits of using LINQ
LINQ enables us to work with these different data sources using a similar coding style without
having the need to know the syntax specific to the data source. In our upcoming videos we will
discuss querying different data sources using LINQ.
Another benefit of using LINQ is that it provides intellisense and compile time error checking.
3|Page
1. LINQ query can be written using any .NET supported programming language
2. LINQ provider is a component between the LINQ query and the actual data source, which
converts the LINQ query into a format that the underlying data source can understand. For
example LINQ to SQL provider converts a LINQ query to T-SQL that SQL Server database can
understand.
For example, the application that we are developing should display male students in a
GridView control as shown below.
To achieve this
Step 1: We first create the required table
Create Table Students
(
ID int primary key identity,
FirstName nvarchar(50),
LastName nvarchar(50),
Gender nvarchar(50)
)
GO
4|Page
Insert into Students values ('Ben', 'Hoskins', 'Male')
Insert into Students values ('Philip', 'Hastings', 'Male')
Insert into Students values ('Mary', 'Lambeth', 'Female')
GO
Step 2: Write the required ADO.NET code to retrieve data from SQL Server database as shown
below.
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.SqlClient;
namespace Demo
{
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string cs = ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
SqlConnection con = new SqlConnection(cs);
SqlCommand cmd = new SqlCommand
("Select ID, FirstName, LastName, Gender from Students where Gender='Male'",
con);
List<Student> listStudents = new List<Student>();
con.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
Student student = new Student();
student.ID = Convert.ToInt32(rdr["ID"]);
student.FirstName = rdr["FirstName"].ToString();
student.LastName = rdr["LastName"].ToString();
student.Gender = rdr["Gender"].ToString();
listStudents.Add(student);
}
con.Close();
GridView1.DataSource = listStudents;
GridView1.DataBind();
}
}
5|Page
public class Student
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
}
}
If we misspell table or column names in the SQL Query, we will not know about it at compile
time. At run time the page crashes and that's when we will know about this error. Also notice
that there is no intellisense when typing table and column names. Misspelled column names
when reading from the reader will also cause the same problem. With LINQ we will have
intellisense and compile time error checking.
Step 3: In "Server Explorer" window, right click on "Data Connections" and select"Add
Connection" option
Step 4: Specify your SQL Server name and the credentials to connect to SQL Server. At this
point we should be connected to SQL Server from Visual Studio.
6|Page
Step 6: From "Server Explorer" window drag and drop "Students" table
onto"Sample.dbml" designer file.
Step 8: Copy and paste the following code in the code-behind file
using System;
using System.Linq;
namespace Demo
{
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
SampleDataContext dataContext = new SampleDataContext();
GridView1.DataSource = from student in dataContext.Students
where student.Gender == "Male"
select student;
GridView1.DataBind();
}
7|Page
}
}
Notice that, with LINQ we are getting intellisense. If we misspell the table or column names we
will get to know about them at compile time. Open SQL Profiler. Run the application, and notice
the SQL Query that is generated.
To write LINQ queries we use the LINQ Standard Query Operators. The following are a few
Examples of Standard Query Operators
select
from
where
orderby
join
groupby
There are 2 ways to write LINQ queries using these Standard Query Operators
1. Using Lambda Expressions. We discussed Lambda Expressions in detail in Part 99 ofC#
Tutorial
For now let's focus on the 2 ways of writing a LINQ query. From a performance perspective
there is no difference between the two. Which one to use depends on your personal preference.
But keep in mind, behind the scene, LINQ queries written using SQL like query expressions are
translated into their lambda expressions before they are compiled.
We will use the following Student class in this demo. GetAllStudents() is a static method that
returns List<Student>. Since List<T> implements IEnumerable<T>, the LINQ Standard Query
Operators will be available and can be applied on List<Student>.
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
8|Page
Student student1 = new Student
{
ID = 101,
Name = "Mark",
Gender = "Male"
};
listStudents.Add(student1);
return listStudents;
}
}
9|Page
IEnumerable<Student> students = from student in Student.GetAllStudents()
where student.Gender == "Male"
select student;
For client code written in C# and Visual Basic, there is no apparent difference between
calling an extension method and the methods that are actually defined in a type.
In spite of Where() method not belonging to List<T> class, we are still able to use it as though it
belong to List<T> class. This is possible because Where() method is implemented as extension
method in IEnumerable<T> interface and List<T> implementsIEnumerable<T> interface.
We want to be able to call this function on the string object as shown below.
string result = strName.ChangeFirstLetterCase();
10 | P a g e
public static string ChangeFirstLetterCase(string inputString)
{
if (inputString.Length > 0)
{
char[] charArray = inputString.ToCharArray();
charArray[0] = char.IsUpper(charArray[0]) ?
char.ToLower(charArray[0]) : char.ToUpper(charArray[0]);
return new string(charArray);
}
return inputString;
}
Wrapper class works, but the problem is, we cannot call ChangeFirstLetterCase() method
using the following syntax.
string result = strName.ChangeFirstLetterCase();
With these 2 changes, we should be able to call this extension method in the same way we call
an instance method. Notice that the extension method shows up in the intellisense as well, but
with a different visual clue.
string result = strName.ChangeFirstLetterCase();
Please note that, we should still be able to call this extension method using wrapper class style
syntax. In fact, behind the scene this is how the method actually gets called. Extension methods
are just a syntactic sugar.
string result = StringHelper.ChangeFirstLetterCase(strName);
So, this means we should also be able to call LINQ extension methods (select, where etc),
using wrapper class style syntax. Since all LINQ extension methods are defined in Enumerable
class, the syntax will be as shown below.
List<int> Numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Example 1:
using System;
using System.Linq;
namespace Demo
{
class Program
{
static void Main()
{
int[] Numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
12 | P a g e
double averageOfAllNumbers = Numbers.Average();
double averageOfAllEvenNumbers = Numbers.Where(n => n % 2 == 0).Average();
Example 2:
using System;
using System.Linq;
namespace Demo
{
class Program
{
static void Main()
{
string[] countries = { "India", "USA", "UK" };
Console.WriteLine
("The shortest country name has {0} characters in its name", minCount);
Console.WriteLine
("The longest country name has {0} characters in its name", maxCount);
}
}
}
In this video we will discuss the use of Aggregate() LINQ function. In Part 4 of LINQ Tutorial,
we discussed the following functions.
Min
13 | P a g e
Max
Sum
Count
Average
We want to combine all these strings into a single comma separated string. The output of the
program should be as shown below.
India, US, UK, Canada, Australia
Console.WriteLine(result);
}
}
}
14 | P a g e
string result = countries.Aggregate((a, b) => a + ", " + b);
Console.WriteLine(result);
}
}
}
This goes on until the last element in the array to produce the final single string "India, US, UK,
Canada, Australia"
Without LINQ
using System;
namespace Demo
{
class Program
{
static void Main()
{
int[] Numbers = { 2, 3, 4, 5 };
int result = 1;
foreach (int i in Numbers)
{
result = result * i;
}
Console.WriteLine(result);
}
}
}
With LINQ:
using System;
using System.Linq;
namespace Demo
{
class Program
{
static void Main()
{
15 | P a g e
int[] Numbers = { 2, 3, 4, 5 };
Console.WriteLine(result);
}
}
}
One of the overloaded version of Aggregate() function has a Seed parameter. If we pass10 as
the value for Seed parameter
int result = Numbers.Aggregate(10, (a, b) => a * b);
The following are the 2 overloaded versions of WHERE extension method in Enumerable
class
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate);
What is a Predicate?
A predicate is a function to test each element for a condition
16 | P a g e
In the following example, the Lambda expression (num => num % 2 == 0) runs for each element
in List<int>. If the number is divisible by 2, then a boolean value true is returned otherwise false.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
IEnumerable<int> evenNumbers = numbers.Where(num => num % 2 == 0);
When you hover the mouse ove WHERE method in the above example, visual studio
intellisense shows the following. Notice that in this case, the predicate expects an int input
parameter and returns a boolean value. The lambda expression that is passed operates on an
int type and should return boolean, otherwise there will be compile time error.
17 | P a g e
So this means, the line below from the above example
IEnumerable<int> evenNumbers = numbers.Where(num => num % 2 == 0);
or like below
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Example 2:
The int parameter of the predicate function represents the index of the source element
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
Func<TSource, int, bool> predicate);
The following program prints the index position of all the even numbers
using System;
18 | P a g e
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Example 3:
Use the following SQL to create Departments and Employees tables
19 | P a g e
Insert into Employees values ('Steve', 'Pound', 'Male', 45000, 3)
Insert into Employees values ('Ben', 'Hoskins', 'Male', 70000, 1)
Insert into Employees values ('Philip', 'Hastings', 'Male', 45000, 2)
Insert into Employees values ('Mary', 'Lambeth', 'Female', 30000, 2)
Insert into Employees values ('Valarie', 'Vikings', 'Female', 35000, 3)
Insert into Employees values ('John', 'Stanmore', 'Male', 80000, 1)
GO
Write a LINQ query to retrieve IT and HR department names and all the male
employees with in these 2 departments.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Program
{
static void Main()
{
EmployeeDBContext context = new EmployeeDBContext();
20 | P a g e
Output:
The following 2 standard LINQ query operators belong to Projection Operators category.
Select
SelectMany
Projection Operators (Select & SelectMany) are used to transform the results of a query. In
this video we will discuss Select operator and in a later video session we will
discuss SelectMany operator.
Select clause in SQL allows to specify what columns we want to retrieve. In a similar fashion
LINQ SELECT standard query operator allows us to specify what properties we want to retrieve.
It also allows us to perform calculations.
For example, you may have a collection of Employee objects. The following are the properties
of the Employee class.
EmployeeID
FirstName
LastName
AnnualSalay
Gender
21 | P a g e
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public int AnnualSalary { get; set; }
return listEmployees;
}
}
22 | P a g e
{
Console.WriteLine(id);
}
Output:
Example 2: Projects FirstName & Gender properties of all employees into anonymous type.
var result = Employee.GetAllEmployees().Select(emp => new
{
FirstName = emp.FirstName,
Gender = emp.Gender
});
foreach (var v in result)
{
Console.WriteLine(v.FirstName + " - " + v.Gender);
}
Output:
Example 3: Computes FullName and MonthlySalay of all employees and projects these 2
new computed properties into anonymous type.
var result = Employee.GetAllEmployees().Select(emp => new
{
FullName = emp.FirstName + " " + emp.LastName,
MonthlySalary = emp.AnnualSalary / 12
});
Output:
23 | P a g e
Example 4: Give 10% bonus to all employees whose annual salary is greater than 50000and
project all such employee's FirstName, AnnualSalay and Bonus into anonymous type.
var result = Employee.GetAllEmployees()
.Where(emp => emp.AnnualSalary > 50000)
.Select(emp => new
{
Name = emp.FirstName,
Salary = emp.AnnualSalary,
Bonus = emp.AnnualSalary * .1
});
Output:
24 | P a g e
List<Student> listStudents = new List<Student>
{
new Student
{
Name = "Tom",
Gender = "Male",
Subjects = new List<string> { "ASP.NET", "C#" }
},
new Student
{
Name = "Mike",
Gender = "Male",
Subjects = new List<string> { "ADO.NET", "C#", "AJAX" }
},
new Student
{
Name = "Pam",
Gender = "Female",
Subjects = new List<string> { "WCF", "SQL Server", "C#" }
},
new Student
{
Name = "Mary",
Gender = "Female",
Subjects = new List<string> { "WPF", "LINQ", "ASP.NET" }
},
};
return listStudents;
}
}
Output:
25 | P a g e
Example 2: Rewrite Example1 using SQL like syntax. When using SQL like syntax style, we
don't use SelectMany, instead we will have an additional from clause, which will get it's data
from the results of the first from clause.
Output:
Same output as in Example 1
string[] stringArray =
{
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"0123456789"
};
26 | P a g e
Output:
27 | P a g e
{
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"0123456789"
};
Output:
Same output as in Example 3
Output:
Output:
Same output as in Example 5
28 | P a g e
Example 7: Selects student name along with all the subjects
var result = Student.GetAllStudetns().SelectMany(s => s.Subjects, (student, subject) =>
new { StudentName = student.Name, Subject = subject });
Output:
Output:
Same output as in Example 7
We will be using the following Student class in this demo. Subjects property in this class is a
collection of strings.
public class Student
{
29 | P a g e
public string Name { get; set; }
public string Gender { get; set; }
public List<string> Subjects { get; set; }
return listStudents;
}
}
In this example, the Select() method returns List of List<string>. To print all the subjects we
will have to use 2 nested foreach loops.
IEnumerable<List<string>> result = Student.GetAllStudetns().Select(s => s.Subjects);
foreach (List<string> stringList in result)
{
foreach (string str in stringList)
{
Console.WriteLine(str);
}
}
SelectMany() on the other hand, flattens queries that return lists of lists into a single list.So in
this case to print all the subjects we have to use just one foreach loop.
30 | P a g e
IEnumerable<string> result = Student.GetAllStudetns().SelectMany(s => s.Subjects);
foreach (string str in result)
{
Console.WriteLine(str);
}
Output:
31 | P a g e
StudentID= 101,
Name = "Tom",
TotalMarks = 800
},
new Student
{
StudentID= 102,
Name = "Mary",
TotalMarks = 900
},
new Student
{
StudentID= 103,
Name = "Valarie",
TotalMarks = 800
},
new Student
{
StudentID= 104,
Name = "John",
TotalMarks = 800
},
};
return listStudents;
}
}
Output:
32 | P a g e
Console.WriteLine(student.Name);
}
Output:
Same as in Example 1
Output:
Output:
Same as in Example 1
The following 5 standard LINQ query operators belong to Ordering Operators category
OrderBy
OrderByDescending
ThenBy
ThenByDescending
Reverse
In Part 10, we discussed OrderBy & OrderByDescending operators. In this video we will
discuss
33 | P a g e
ThenBy
ThenByDescending
Reverse
return listStudents;
34 | P a g e
}
}
OrderBy or OrderByDescending work fine when we want to sort a collection just by one value
or expression.
Example 1:
a) Sorts Students first by TotalMarks in ascending order(Primary Sort)
b) The 4 Students with TotalMarks of 800, will then be sorted by Name in ascending order
(First Secondary Sort)
c) The 2 Students with Name of John, will then be sorted by StudentID in ascending order
(Second Secondary Sort)
Output:
Example 2: Rewrite Example 1 using SQL like syntax. With SQL like syntax we donot
use ThenBy or ThenByDescending, instead we specify the sort expressions using a comma
separated list. The first sort expression will be used for primary sort and the subsequent sort
expressions for secondary sort.
35 | P a g e
Example 3: Reverses the items in the collection.
Console.WriteLine();
IEnumerable<Student> result = students.Reverse();
Output:
Take method returns a specified number of elements from the start of the collection. The
number of items to return is specified using the count parameter this method expects.
36 | P a g e
Skip method skips a specified number of elements in a collection and then returns the
remaining elements. The number of items to skip is specified using the count parameter this
method expects.
Please Note: For the same argument value, the Skip method returns all of the items that the
Take method would not return.
TakeWhile method returns elements from a collection as long as the given condition specified
by the predicate is true.
SkipWhile method skips elements in a collection as long as the given condition specified by the
predicate is true, and then returns the remaining elements.
Output:
Example 3: Skips the first 3 countries and retrieves the rest of them
37 | P a g e
foreach (string country in result)
{
Console.WriteLine(country);
}
Output:
Example 4: Return countries starting from the beginning of the array until a country name is hit
that does not have length greater than 2 characters.
Output:
Example 5: Skip elements starting from the beginning of the array, until a country name is hit
that does not have length greater than 2 characters, and then return the remaining elements.
38 | P a g e
Output:
In this video, we will discuss implementing paging using Skip and Take operators in LINQ.
We will use the following Student class in this demo. Notice that, there are 11 total Students.
We want to display a maximum of 3 students per page. So there will be 4 total pages. The
last page, i.e Page 4 will display the last 2 students.
public class Student
{
public int StudentID { get; set; }
public string Name { get; set; }
public int TotalMarks { get; set; }
return listStudents;
}
}
39 | P a g e
2. If the user does not enter a valid page number, the program should prompt the user to enter a
valid page number.
3. Once a valid page number is entered, the program should display the correct set of Students
The following console application use Skip() and Take() operators to achieve this.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Program
{
public static void Main()
{
IEnumerable<Student> students = Student.GetAllStudetns();
do
{
Console.WriteLine("Please enter Page Number - 1,2,3 or 4");
int pageNumber = 0;
40 | P a g e
{
int pageSize = 3;
IEnumerable<Student> result = students
.Skip((pageNumber - 1) * pageSize).Take(pageSize);
Console.WriteLine();
Console.WriteLine("Displaying Page " + pageNumber);
foreach (Student student in result)
{
Console.WriteLine(student.StudentID + "\t" +
student.Name + "\t" + student.TotalMarks);
}
Console.WriteLine();
}
else
{
Console.WriteLine("Page number must be an integer between 1 and 4");
}
}
else
{
Console.WriteLine("Page number must be an integer between 1 and 4");
}
} while (1 == 1);
}
}
}
Please Note: The condition in the while loop puts the program in an infinite loop. To end the
program, simply close the console window.
LINQ operators can be broadly classified into 2 categories based on the behaviour of
query execution
1. Deferred or Lazy Operators - These query operators use deferred execution.
Examples - select, where, Take, Skip etc
2. Immediate or Greedy Operators - These query operators use immediate execution.
Examples - count, average, min, max, ToList etc
41 | P a g e
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
public class Student
{
public int StudentID { get; set; }
public string Name { get; set; }
public int TotalMarks { get; set; }
}
class Program
{
public static void Main()
{
List<Student> listStudents = new List<Student>
{
new Student { StudentID= 101, Name = "Tom", TotalMarks = 800 },
new Student { StudentID= 102, Name = "Mary", TotalMarks = 900 },
new Student { StudentID= 103, Name = "Pam", TotalMarks = 800 }
};
// LINQ Query is only defined here and is not executed at this point
// If the query is executed at this point, the result should not display Tim
IEnumerable<Student> result = from student in listStudents
where student.TotalMarks == 800
select student;
// The above query is actually executed when we iterate thru the sequence
// using the foreach loop. This is proved as Tim is also included in the result
foreach (Student s in result)
{
Console.WriteLine(s.StudentID + "\t" + s.Name + "\t" + s.TotalMarks);
}
}
}
}
Output:
42 | P a g e
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
public class Student
{
public int StudentID { get; set; }
public string Name { get; set; }
public int TotalMarks { get; set; }
}
class Program
{
public static void Main()
{
List<Student> listStudents = new List<Student>
{
new Student { StudentID= 101, Name = "Tom", TotalMarks = 800 },
new Student { StudentID= 102, Name = "Mary", TotalMarks = 900 },
new Student { StudentID= 103, Name = "Pam", TotalMarks = 800 }
};
Output:
43 | P a g e
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
public class Student
{
public int StudentID { get; set; }
public string Name { get; set; }
public int TotalMarks { get; set; }
}
class Program
{
public static void Main()
{
List<Student> listStudents = new List<Student>
{
new Student { StudentID= 101, Name = "Tom", TotalMarks = 800 },
new Student { StudentID= 102, Name = "Mary", TotalMarks = 900 },
new Student { StudentID= 103, Name = "Pam", TotalMarks = 800 }
};
// Since we are using Count() operator, the LINQ Query is executed at this point
int result = (from student in listStudents
where student.TotalMarks == 800
select student).Count();
Output:
The following standard LINQ query operators belong to Conversion Operators category
ToList
44 | P a g e
ToArray
ToDictionary
ToLookup
Cast
OfType
AsEnumerable
AsQueryable
ToList operator extracts all of the items from the source sequence and returns a newList<T>.
This operator causes the query to be executed immediately. This operator does not use
deferred execution.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Program
{
public static void Main()
{
int[] numbers = { 1, 2, 3, 4, 5 };
Output:
ToArray operator extracts all of the items from the source sequence and returns a new Array.
This operator causes the query to be executed immediately. This operator does not use
deferred execution.
45 | P a g e
Example 2: Convert List<string> to string array. The items in the array should be sorted in
ascending order.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Program
{
public static void Main()
{
List<string> countries = new List<string> { "US", "India", "UK", "Australia","Canada" };
Output:
ToDictionary operator extracts all of the items from the source sequence and returns a new
Dictionary. This operator causes the query to be executed immediately. This operator does not
use deferred execution.
Example 3 : Convert List<Student> to a Dictionary. StudentID should be the key and Name
should be the value. In this example, we are using the overloaded of ToDictionary() that takes 2
parameters
a) keySelector - A function to extract a key from each element
b) elementSelector - A function to produce a result element from each element in the
sequence
using System;
using System.Collections.Generic;
46 | P a g e
using System.Linq;
namespace Demo
{
public class Student
{
public int StudentID { get; set; }
public string Name { get; set; }
public int TotalMarks { get; set; }
}
class Program
{
public static void Main()
{
List<Student> listStudents = new List<Student>
{
new Student { StudentID= 101, Name = "Tom", TotalMarks = 800 },
new Student { StudentID= 102, Name = "Mary", TotalMarks = 900 },
new Student { StudentID= 103, Name = "Pam", TotalMarks = 800 }
};
Output:
Example 4 : Convert List<Student> to a Dictionary. StudentID should be the key and Student
object should be the value. In this example, we are using the overloaded of ToDictionary() that
takes 1 parameter
a) keySelector - A function to extract a key from each element
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
47 | P a g e
public class Student
{
public int StudentID { get; set; }
public string Name { get; set; }
public int TotalMarks { get; set; }
}
class Program
{
public static void Main()
{
List<Student> listStudents = new List<Student>
{
new Student { StudentID= 101, Name = "Tom", TotalMarks = 800 },
new Student { StudentID= 102, Name = "Mary", TotalMarks = 900 },
new Student { StudentID= 103, Name = "Pam", TotalMarks = 800 }
};
Output:
Please Note: Keys in the dictionary must be unique. If two identical keys are created by the
keySelector function, the following System.ArgumentException will be thrown at runtime.
Unhandled Exception: System.ArgumentException: An item with the same key has already
been added.
ToLookup creates a Lookup. Just like a dictionary, a Lookup is a collection of key/value pairs.
A dictionary cannot contain keys with identical values, where as a Lookup can.
Example 5: Create 2 Lookups. First lookup should group Employees by JobTitle, and second
lookup should group Employees by City.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
48 | P a g e
{
public class Employee
{
public string Name { get; set; }
public string JobTitle { get; set; }
public string City { get; set; }
}
class Program
{
public static void Main()
{
List<Employee> listEmployees = new List<Employee>
{
new Employee() { Name = "Ben", JobTitle = "Developer", City = "London" },
new Employee() { Name = "John", JobTitle = "Sr. Developer", City ="Bangalore" },
new Employee() { Name = "Steve", JobTitle = "Developer", City = "Bangalore" },
new Employee() { Name = "Stuart", JobTitle = "Sr. Developer", City = "London"},
new Employee() { Name = "Sara", JobTitle = "Developer", City = "London" },
new Employee() { Name = "Pam", JobTitle = "Developer", City = "London" }
};
Console.WriteLine(); Console.WriteLine();
49 | P a g e
}
}
}
Output:
50 | P a g e
We discussed the following operators in Part 15
ToList
ToArray
ToDictionary
ToLookup
Cast operator attempts to convert all of the items within an existing collection to another type
and return them in a new collection. If an item fails conversion an exception will be thrown. This
method uses deferred execution.
Example :
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Program
{
public static void Main()
{
ArrayList list = new ArrayList();
list.Add(1);
list.Add(2);
list.Add(3);
Output :
51 | P a g e
OfType operator will return only elements of the specified type. The other type elements are
simply ignored and excluded from the result set.
Example : In the example below, items "4" and "ABC" will be ignored from the result set. No
exception will be thrown.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Program
{
public static void Main()
{
ArrayList list = new ArrayList();
list.Add(1);
list.Add(2);
list.Add(3);
list.Add("4");
list.Add("ABC");
Output :
52 | P a g e
collection will be ignored and excluded from the result.
Cast operator will try to cast all the elements in the collection into the specified type. If some of
the items fail conversion, InvalidCastException will be thrown.
If we want to filter the elements and return only the ones of the specified type, then we would
use OfType.
The main use of AsQueryable operator is unit testing to mock a queryable data source using
an in-memory data source. We will discuss this operator in detail with examples in unit testing
video series.
AsEnumerable operator: Let us understand the use of this operator with an example. We will
be using the following Employees table in this demo.
Step 1: Execute the following SQL Script to create and populate Employees Table
53 | P a g e
Create Table Employees
(
ID int primary key identity,
Name nvarchar(50),
Gender nvarchar(50),
Salary int
)
GO
Step 3: Right click on the Demo project in Solution Explorer and Add a new LINQ to SQL
Classes. Name it EmployeeDB.dbml.
Step 4: Click on View menu, and select "Server Explorer". Expand Data Connectionsand
then Drag and Drop Employees table onto EmployeeDB.dbml designer surface.
Step 5: Copy and paste the following code in Program.cs file. The linq query in this sample,
retrieves the TOP 5 Male Employees By Salary.
using System;
using System.Linq;
namespace Demo
{
class Program
{
public static void Main()
{
EmployeeDBDataContext dbContext = new EmployeeDBDataContext();
// TOP 5 Male Employees By Salary
var result = dbContext.Employees.Where(x => x.Gender == "Male")
.OrderByDescending(x => x.Salary).Take(5);
54 | P a g e
}
}
Step 6: Now open SQL Profiler and run a new trace and then run the console application.
Step 7: Notice that the following SQL Query is executed against the database.
exec sp_executesql N'SELECT TOP (5) [t0].[ID], [t0].[Name], [t0].[Gender], [t0].[Salary]
FROM [dbo].[Employees] AS [t0]
WHERE [t0].[Gender] = @p0
ORDER BY [t0].[Salary] DESC',N'@p0 nvarchar(4000)',@p0=N'Male'
FROM
var result = dbContext.Employees.Where(x => x.Gender == "Male")
.OrderByDescending(x => x.Salary).Take(5);
TO
var result = dbContext.Employees.AsEnumerable()
.Where(x => x.Gender == "Male")
.OrderByDescending(x => x.Salary).Take(5);
Step 9: Run the console application and notice the query generated in SQL Profiler.
SELECT [t0].[ID], [t0].[Name], [t0].[Gender], [t0].[Salary]
FROM [dbo].[Employees] AS [t0]
Summary:
55 | P a g e
So in this example the following SQL Query is executed against SQL Server, all the data is
brought into the console application and then the WHERE, ORDERBY & TOP operators are
applied on the client-side
SELECT [t0].[ID], [t0].[Name], [t0].[Gender], [t0].[Salary]
FROM [dbo].[Employees] AS [t0]
So in short, use AsEnumerable operator to move query processing to the client side.
56 | P a g e
Department = "HR", Salary = 84000 },
new Employee { ID = 10, Name = "Andy", Gender = "Male",
Department = "IT", Salary = 36000 }
};
}
}
Output:
Example 2: Get Employee Count By Department and also each employee and department
name
var employeeGroup = from employee in Employee.GetAllEmployees()
group employee by employee.Department;
Output:
57 | P a g e
Example 3: Get Employee Count By Department and also each employee and department
name. Data should be sorted first by Department in ascending order and then by Employee
Name in ascending order.
var employeeGroup = from employee in Employee.GetAllEmployees()
group employee by employee.Department into eGroup
orderby eGroup.Key
select new
{
Key = eGroup.Key,
Employees = eGroup.OrderBy(x => x.Name)
};
Output:
Let us understand this with an example. We will be using the following Employee class in this
demo. This is the same class used in Part 18. Please watch Part 18 before proceeding.
public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
58 | P a g e
public string Gender { get; set; }
public string Department { get; set; }
Example 1: Group employees by Department and then by Gender. The employee groups
should be sorted first by Department and then by Gender in ascending order. Also, employees
within each group must be sorted in ascending order by Name.
59 | P a g e
foreach (var employee in group.Employees)
{
Console.WriteLine(employee.Name + "\t" + employee.Gender
+ "\t" + employee.Department);
}
Console.WriteLine(); Console.WriteLine();
}
Output:
60 | P a g e
ElementAt / ElementAtOrDefault
Single / SingleOrDefault
DefaultIfEmpty
Element Operators retrieve a single element from a sequence using the element index or
based on a condition. All of these methods have a corresponding overloaded version that
accepts a predicate.
First : There are 2 overloaded versions of this method. The first overloaded version that does
not have any parameters simply returns the first element of a sequence.
Output:
Result = 1
If the sequence does not contain any elements, then First() method throws an
InvalidOperationException.
Output:
Unhandled Exception: System.InvalidOperationException: Sequence contains no elements
The second overloaded version is used to find the first element in a sequence based on a
condition. If the sequence does not contain any elements or if no element in the sequence
satisfies the condition then an InvalidOperationException is thrown.
Output:
Result = 2
Output:
61 | P a g e
Unhandled Exception: System.InvalidOperationException: Sequence contains no matching
element
FirstOrDefault : This is very similar to First, except that this method does not throw an
exception when there are no elements in the sequence or when no element satisfies the
condition specified by the predicate. Instead, a default value of the type that is expected is
returned. For reference types the default is NULL and for value types the default depends on
the actual type expected.
Example 5: Returns ZERO. No element in the sequence satisfies the condition, so the default
value (ZERO) for int is returned.
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int result = numbers.FirstOrDefault(x => x % 2 == 100);
Console.WriteLine("Result = " + result);
Last : Very similar to First, except it returns the last element of the sequence.
LastOrDefault : Very similar to FirstOrDefault, except it returns the last element of the
sequence.
ElementAt : Returns an element at a specified index. If the sequence is empty or if the provided
index value is out of range, then an ArgumentOutOfRangeException is thrown.
Output:
Result = 2
Output:
Unhandled Exception: System.ArgumentOutOfRangeException: Index was out of range. Must
be non-negative and less than the size of the collection.
ElementAtOrDefault : Similar to ElementAt except that this method does not throw an
exception, if the sequence is empty or if the provided index value is out of range. Instead, a
default value of the type that is expected is returned.
Single : There are 2 overloaded versions of this method. The first overloaded version that does
not have any parameters returns the only element of the sequence.
62 | P a g e
Output:
Result = 1
Single() method throws an exception if the sequence is empty or has more than one element.
Output:
Unhandled Exception: System.InvalidOperationException: Sequence contains more than one
element
The second overloaded version of the Single() method is used to find the only element in
a sequence that satisfies a given condition. An exception will be thrown if any of the
following is true
a) If the sequence does not contain any elements OR
b) If no element in the sequence satisfies the condition OR
c) If more than one element in the sequence satisfies the condition
Example 10: Throws InvalidOperationException as more than one element in the sequence
satisfies the condition
int[] numbers = { 1, 2, 4 };
int result = numbers.Single(x => x % 2 == 0);
Console.WriteLine("Result = " + result);
Output:
Unhandled Exception: System.InvalidOperationException: Sequence contains more than one
matching element
SingleOrDefault : Very similar to Single(), except this method does not throw an exception
when the sequence is empty or when no element in the sequence satisfies the given condition.
Just like Single(), this method will still throw an exception, if more than one element in the
sequence satisfies the given condition.
Example 11: Throws InvalidOperationException as more than one element in the sequence
satisfies the given condition
int[] numbers = { 1, 2, 4 };
int result = numbers.SingleOrDefault(x => x % 2 == 0);
Console.WriteLine("Result = " + result);
Output:
Unhandled Exception: System.InvalidOperationException: Sequence contains more than one
matching element
DefaultIfEmpty : If the sequence on which this method is called is not empty, then the values of
the original sequence are returned.
63 | P a g e
Example 12 : Returns a copy of the original sequence
int[] numbers = { 1, 2, 3 };
IEnumerable<int> result = numbers.DefaultIfEmpty();
foreach (int i in result)
{
Console.WriteLine(i);
}
Output:
1
2
3
If the sequence is empty, then DefaultIfEmpty() returns a sequence with the default value of
the expected type.
Example 13 : Since the sequence is empty, a sequence containing the default value (ZERO) of
int is returned.
int[] numbers = { };
IEnumerable<int> result = numbers.DefaultIfEmpty();
foreach (int i in result)
{
Console.WriteLine(i);
}
Output:
0
The other overloaded version with a parameter allows us to specify a default value. If this
method is called on a sequence that is not empty, then the values of the original sequence are
returned. If the sequence is empty, then this method returns a sequence with the specified
defualt value.
Example 14 : Since the sequence is empty, a sequence containing the specified default value
(10) is returned.
int[] numbers = { };
IEnumerable<int> result = numbers.DefaultIfEmpty(10);
foreach (int i in result)
{
Console.WriteLine(i);
}
Output:
10
64 | P a g e
Inner Join - Discussed in Part 22
Left Outer Join
Cross Join
In this video, we will discuss Group Join. Group Join produces hierarchical data structures.
Each element from the first collection is paired with a set of correlated elements from the
second collection.
65 | P a g e
}
}
Output:
Please note: Group Join uses the join operator and the into keyword to group the results of the
join.
Part 22 - Inner Join in LINQ
66 | P a g e
The following are the different types of joins in LINQ
Group Join - Discussed in Part 21
Inner Join - We will discuss in this video
Left Outer Join - Later Video
Cross Join - Later Video
In this video we will discuss implementing INNER JOIN in LINQ. If you have 2 collections, and
when you perform an inner join, then only the matching elements between the 2 collections are
included in the result set. Non - Matching elements are excluded from the result set.
67 | P a g e
new Employee { ID = 9, Name = "Stacey", DepartmentID = 2 },
new Employee { ID = 10, Name = "Andy"}
};
}
}
Example 1 : Join the Employees and Department collections and print all the Employees and
their respective department names.
var result = Employee.GetAllEmployees().Join(Department.GetAllDepartments(),
e => e.DepartmentID,
d => d.ID, (employee, department) => new
{
EmployeeName = employee.Name,
DepartmentName = department.Name
});
foreach (var employee in result)
{
Console.WriteLine(employee.EmployeeName + "\t" + employee.DepartmentName);
}
Output: Notice that, in the output we don't have Andy record. This is because, Andy does not
have a matching department in Department collection. So this is effectively an inner join.
68 | P a g e
public class Department
{
public int ID { get; set; }
public string Name { get; set; }
69 | P a g e
The following query performs a GroupJoin on the 2 lists
var result = from d in Department.GetAllDepartments()
join e in Employee.GetAllEmployees()
on d.ID equals e.DepartmentID into eGroup
select new
{
Department = d,
Employees = eGroup
};
Notice that we are using the join operator and the into keyword to group the results of the join.
To perform group join using extension method syntax, we use GroupJoin() Extension method
as shown below.
var result = Department.GetAllDepartments()
.GroupJoin(Employee.GetAllEmployees(),
d => d.ID,
e => e.DepartmentID,
(department, employees) => new
{
Department = department,
Employees = employees
});
The above 2 queries groups employees by department and would produce the following
groups.
70 | P a g e
To print the Department and Employee Names we use 2 foreach loops as shown below.
foreach (var department in result)
{
Console.WriteLine(department.Department.Name);
foreach (var employee in department.Employees)
{
Console.WriteLine(" " + employee.Name);
}
Console.WriteLine();
}
To perform an inner join using extension method syntax, we use Join() Extension method as
shown below.
var result = Employee.GetAllEmployees()
.Join(Department.GetAllDepartments(),
e => e.DepartmentID,
d => d.ID, (employee, department) => new
{
e = employee,
d = department
});
71 | P a g e
The above 2 queries would produce a flat result set as shown below
To print the Department and Employee Names we use just 1 foreach loop as shown below.
foreach (var employee in result)
{
Console.WriteLine(employee.e.Name + "\t" + employee.d.Name);
In short, Join is similar to INNER JOIN in SQL and GroupJoin is similar to OUTER JOINin
SQL
72 | P a g e
In this video we will discuss implementing LEFT OUTER JOIN in LINQ.
With INNER JOIN only the matching elements are included in the result set. Non-matching
elements are excluded from the result set.
With LEFT OUTER JOIN all the matching elements + all the non matching elements from the
left collection are included in the result set.
Let us understand implementing Left Outer Join with an example. Consider the
followingDepartment and Employee classes. Notice that, Employee Mary does not have a
department assigned. An inner join will not include her record in the result set, where as a Left
Outer Join will.
73 | P a g e
}
}
Use DefaultIfEmpty() method on the results of a group join to implement Left Outer Join
Output: Notice that, we also have Mary record in spite of she not having a department. So this
is effectively a left outer join.
74 | P a g e
Example 2 : Rewrite Example 1 using extension method syntax.
var result = Employee.GetAllEmployees()
.GroupJoin(Department.GetAllDepartments(),
e => e.DepartmentID,
d => d.ID,
(emp, depts) => new { emp, depts })
.SelectMany(z => z.depts.DefaultIfEmpty(),
(a, b) => new
{
EmployeeName = a.emp.Name,
DepartmentName = b == null ? "No Department" : b.Name
});
To implement Left Outer Join, with extension method syntax we use the GroupJoin()method
along with SelectMany() and DefaultIfEmpty() methods.
Cross join produces a cartesian product i.e when we cross join two sequences, every element
in the first collection is combined with every element in the second collection. The total number
of elements in the resultant sequence will always be equal to the product of the elements in the
two source sequences. The on keyword that specfies the JOIN KEY is not required.
75 | P a g e
return new List<Department>()
{
new Department { ID = 1, Name = "IT"},
new Department { ID = 2, Name = "HR"},
};
}
}
76 | P a g e
Example 2 : Cross Join Departments collections with Employees collection
var result = from d in Department.GetAllDepartments()
from e in Employee.GetAllEmployees()
select new { e, d };
Output: Notice that the output in this case is slightly different from Example 1. In this case,
every element from the Departments collection is combined with every element in the
Employees collection.
77 | P a g e
The following operators belong to Set operators category
Distinct
Union
Intersect
Except
In this video we will discuss Distinct operator. This operator returns distinct elements from a
given collection.
Example 1: Return distinct country names. In this example the default comparer is being used
and the comparison is case-sensitive, so in the output we see country USA 2 times.
Output:
Example 2: For the comparison to be case-insensitive, use the other overloaded version
of Distinct() method to which we can pass a class that implementsIEqualityComparer as an
argument. In this case we see country USA only once in the output.
Output:
When comparing elements, Distinct() works in a slightly different manner with complex
types like Employee, Customer etc.
78 | P a g e
Example 3: Notice that in the output we don't get unique employees. This is because, the
default comparer is being used which will just check for object references being equal and not
the individual property values.
Output:
Example 4 : Using the overloaded version of Distinct() method to which we can pass a custom
class that implements IEqualityComparer
79 | P a g e
List<Employee> list = new List<Employee>()
{
new Employee { ID = 101, Name = "Mike"},
new Employee { ID = 101, Name = "Mike"},
new Employee { ID = 102, Name = "Mary"}
};
Output:
80 | P a g e
Console.WriteLine(" " + v.ID + "\t" + v.Name);
}
Union combines two collections into one collection while removing the duplicate elements.
Example 1: numbers1 and numbers2 collections are combined into a single collection. Notice
that, the duplicate elements are removed.
int[] numbers1 = { 1, 2, 3, 4, 5 };
int[] numbers2 = { 1, 3, 6, 7, 8 };
Output:
Example 2 : Notice that in the output the duplicate employee objects are not removed. This is
because, the default comparer is being used which will just check for object references being
equal and not the individual property values.
81 | P a g e
new Employee { ID = 102, Name = "Susy"},
new Employee { ID = 103, Name = "Mary"}
};
Output :
82 | P a g e
Console.WriteLine(v.ID + "\t" + v.Name);
}
Output :
int[] numbers1 = { 1, 2, 3, 4, 5 };
int[] numbers2 = { 1, 3, 6, 7, 8 };
Output :
Except() returns the elements that are present in the first collection but not in the second
collection.
Example 5: Return the elements that are present in the first collection but not in the second
collection.
int[] numbers1 = { 1, 2, 3, 4, 5 };
int[] numbers2 = { 1, 3, 6, 7, 8 };
Output :
83 | P a g e
The following operators belong to Generation Operators category
Range
Repeat
Empty
Range operator generates a sequence of integers within a specified range. This method
has 2 integer parameters. The start parameter specifies the integer to start with and the count
parameter specifies the number of sequential integers to generate.
For example to print the first 10 even numbers without using LINQ, we would use a for loop as
shown below.
for (int i = 1; i <= 10; i++)
{
if (i % 2 == 0)
{
Console.WriteLine(i);
}
}
To achieve the same using LINQ, we can use Range method as shown below.
Output :
Repeat operator is used to generate a sequence that contains one repeated value.
For example the following code returns a string sequence that contains 5 "Hello" string
objects in it.
var result = Enumerable.Repeat("Hello", 5);
Output:
84 | P a g e
Empty operator returns an empty sequence of the specified type. For example
Enumerable.Empty<int>() - Returns an empty IEnumerable<int>
Enumerable.Empty<string>() - Returns an empty IEnumerable<string>
The question that comes to our mind is, what is the use of Empty() method. Here is an
example where we could use Empty() method
There may be scenarios where our application calls a method in a third party application that
returns IEnumerable<int>. There may be a situation where the third party method returns null.
For the purpose of this example, let us assume the third party method is similar
to GetIntegerSequence().
One way to fix this is to check for NULL before looping thru the items in the result as shown
below.
class Program
{
public static void Main()
{
IEnumerable<int> result = GetIntegerSequence();
if (result != null)
{
foreach (var v in result)
{
Console.WriteLine(v);
}
85 | P a g e
}
}
The other way to fix it, is by using Empty() linq method as shown below. Here we are
using NULL-COALESCING operator that checks if the GetIntegerSequence() method returns
NULL, in which case the result variable is initialized with an emptyIEnumerable<int>.
class Program
{
public static void Main()
{
IEnumerable<int> result = GetIntegerSequence() ?? Enumerable.Empty<int>();
The following code will concatenate both the integer sequences (numbers1 & numbers2) into
one integer sequence. Notice that the duplicate elements ARE NOT REMOVED.
int[] numbers1 = { 1, 2, 3 };
int[] numbers2 = { 1, 4, 5 };
86 | P a g e
Console.WriteLine(v);
}
Output :
Now let us perform a union between the 2 integer sequences (numbers1 & numbers2). Just like
concat operator, union operator also combines the 2 integer sequences (numbers1 &
numbers2) into one integer sequence, but notice that the duplicate elements ARE REMOVED.
int[] numbers1 = { 1, 2, 3 };
int[] numbers2 = { 1, 4, 5 };
Output :
Union operator also combines 2 sequences into 1 sequence, but will remove the duplicate
elements.
87 | P a g e
string[] countries1 = { "USA", "India", "UK" };
string[] countries2 = { "USA", "India", "UK" };
Example 2 : In this case, SequenceEqual() returns false, as the default comparison is case
sensitive.
Example 3: If we want the comparison to be case-insensitive, then use the other overloaded
version of SequenceEqual() method to which we can pass an alternate comparer.
Example 4 : SequenceEqual() returns false. This is because, although both the sequences
contain same data, the data is not present in the same order.
Example 5 : To fix the problem in Example 4, use OrderBy() to sort data in the source
sequences.
Example 6 : When comparing complex types, the default comparer will only check if the object
references are equal. So, in this case SequenceEqual() returns false.
88 | P a g e
List<Employee> list1 = new List<Employee>()
{
new Employee { ID = 101, Name = "Mike"},
new Employee { ID = 102, Name = "Susy"},
};
We discussed implementing these 3 options for Distinct() method in Part 26 of LINQ Tutorial. In
the same way these options can be implemented for SequenceEqual() method.
All these methods return true or false depending on whether if some or all of the elements in a
sequence satisfy a condition.
All() method returns true if all the elements in a sequence satisfy a given condition, otherwise
false.
int[] numbers = { 1, 2, 3, 4, 5 };
Console.WriteLine(result);
There are 2 overloaded versions of Any() method. The version without any parameters checks
89 | P a g e
if the sequence contains at least one element. The other version with a predicate parameter
checks if the sequence contains at least one element that satisfies a given condition.
int[] numbers = { 1, 2, 3, 4, 5 };
Console.WriteLine(result);
Example 3 : Returns false as the sequence does not contain any element that satisfies the
given condition (No element in the sequence is greater than 10)
int[] numbers = { 1, 2, 3, 4, 5 };
Console.WriteLine(result);
There are 2 overloaded versions of the Contains() method. One of the overloaded version
checks if the sequence contains a specified element using the default equality comparer. The
other overloaded version checks if the sequence contains a specified element using an
alternate equality comparer.
Example 4 : Returns true as the sequence contains number 3. In this case the default equality
comparer is used.
int[] numbers = { 1, 2, 3, 4, 5 };
Console.WriteLine(result);
Example 5 : Returns true. In this case we are using an alternate equality comparer
(StringComparer) for the comparison to be case-insensitive.
Console.WriteLine(result);
When comparing complex types like Employee, Customer etc, the default comparer will only
check if the object references are equal, and not the individual property values of the objects
that are being compared.
Example 6 : Returns false, as the default comparer will only check if the object references are
equal.
90 | P a g e
{
new Employee { ID = 101, Name = "Rosy"},
new Employee { ID = 102, Name = "Susy"}
};
Console.WriteLine(result);
What is LinqPad
LinqPad is a free tool that you can download from http://www.linqpad.net. It helps learn, write
and test linq queries.
Copy and paste the following LINQ query in LinqPad. To execute the query, you can either
press the Green Execute button on the LinqPad or press F5. Dump() method is similar
to Console.WriteLine() in a console application.
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
91 | P a g e
Notice that the results of the query are shown in the Results window. Next to the results
window, you also have the following options
1. ? (lambda Symbol) - Use this button to get the lambda equivalent of a LINQ Query
2. SQL - Shows the generated SQL statement that will be executed against the underlying
database
3. IL - Shows the Intermediate Language code
For the above query, Lambda and SQL windows will not show anything. To get the Lambda
equivalent of a LINQ query, use .AsQueryable() on the source collection as shown below.
92 | P a g e
AsQueryable() can also be used on the source collection as shown below.
var numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }.AsQueryable();
result.Dump();
LinqPad can also be used with databases and WCF Data Services.
93 | P a g e
Step 4 : Select "SQL Server" as the "Provider"
Step 5 : Specify the Server Name. In my case I am connecting to the local SQL Server. So I
used . (DOT)
Step 6 : Select the Authentication
Step 7 : Select the Database
Step 8 : Click OK
94 | P a g e
At this point LinqPad connects to the database, and shows all the table entities. The
relationships between the entities are also shown. The Green Split arrow indicates One-to-Many
relationship and the Blue Split Arrow indicates Many-to-One relationship.
95 | P a g e
We can now start writing linq queries targeting the SQL Server database.
The following LINQ query fetches all the employee names that start with letter 'M' and sorts
them in descending order
from e in Employees
where e.Name.StartsWith("M")
orderby e.Name descending
select e.Name
After executing the query, click on the SQL button to see the Transact-SQL that is generated.
96 | P a g e
Adding a WCF Data Services connection in LinqPad
Step 1 : Click "Add connection"
Step 2 : Under LinqPad Driver, select "WCF Data Services"
Step 3 : Click Next
97 | P a g e
Step 4 : Type the URI for the WCF Data Service.
http://services.odata.org/V3/Northwind/Northwind.svc/
Step 5 : Click OK
98 | P a g e
We can now start writing linq queries targeting the WCF Data Service.
The following LINQ query fetches all the product names that start with letter 'C' and sorts
them in ascending order
from p in Products
where p.ProductName.StartsWith("C")
orderby p.ProductName ascending
select p
Since LINQ to SQL models a relational database using strongly typed .net classes, we
have the following advantages
1. Intellisense support
2. Compile time error checking
3. Debugging support
99 | P a g e
Modeling Databases - Creating LINQ to SQL classes
Use the LINQ to SQL designer that ships with Visual Studio to create LINQ to SQL classes.
Here are the steps.
Step 2 : Execute the following SQL script to create Departments and Employees tables and
populate them with test data.
Create table Departments
(
ID int primary key identity,
Name nvarchar(50),
Location nvarchar(50)
)
GO
Step 3 : Run Visual Studio as an administrator. Create a new empty asp.net web application
project. Name it Demo.
Step 4 : Right click on the project in solution explorer and add LINQ to SQL Classes. Change
the name from DataClasses1.dbml to Sample.dbml
100 | P a g e
Step 5 : At this point, Sample.dbml file should have been added to the project. Click onServer
Explorer link on Sample.dbml file. In the Server Explorer window, you should find all the
tables in the Sample database. Drag and drop the tables on Sample.dbml file.
Step 6 : At this point we should have Department and Employee classes. The properties of the
class map to the columns of the respective table in the database. The arrow between the
classes represent the association between them. These associations are modeled based on
the primary-key/foreign-key relationships between the tables in the database. Notice that the
arrow is pointing from Department to Employee entity. In this case there is a One-to-Many
relationship between Department and Employee entities. A Department can have 1 or more
101 | P a g e
employees.
Step 7 : Add a WebForm to the project. Drag and Drop a GridView control on the webform.
Step 8 : Copy and paste the following code in the code-behind file.
using System;
using System.Linq;
namespace Demo
{
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
SampleDataContext dbContext = new SampleDataContext();
GridView1.DataSource = from employee in dbContext.Employees
where employee.Gender == "Male"
orderby employee.Salary descending
select employee;
GridView1.DataBind();
}
}
}
We have not written any T-SQL code here. So, how is the application able to retrieve data
from the SQL Server database. Let us understand what is happening behind the scenes.
1. Application issues a LINQ Query
2. LINQ to SQL provider translates the LINQ query into T-SQL that the SQL Server database
can understand
3. SQL Server executes the query, and returns the matching rows
4. LINQ to SQL provider creates Employee objects, populates properties and return the objects
to the application.
102 | P a g e
Part 34 - Insert Update Delete using LINQ to SQL
In this video, we will discuss performing Insert Update and Delete using LINQ to SQL. We
will continue with the example, we worked with in Part 33. In Part 33, we discussed performing a
Select using LINQ to SQL. Please watch Part 33 before proceeding.
103 | P a g e
{
Employee newEmployee = new Employee
{
FirstName = "Tim",
LastName = "T",
Gender = "Male",
Salary = 55000,
DepartmentId = 1
};
dbContext.Employees.InsertOnSubmit(newEmployee);
dbContext.SubmitChanges();
}
Now let us continue with the example that we worked with in Part 1.
Step 1 : Drag and drop 4 button controls on the webform. Change the Text & IDproperties of
the 4 button controls as shown below.
Button 1 : Text = Get Data, ID = btnGetData
Button 2 : Text = Insert, ID = btnInsert
Button 3 : Text = Update, ID = btnUpdate
Button 4 : Text = Delete, ID = btnDelete
Double click on each of the button controls to generate their respective click event handler
methods.
104 | P a g e
Step 2 : Copy and paste the following code in the code-behind file.
using System;
using System.Linq;
namespace Demo
{
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{}
dbContext.Employees.InsertOnSubmit(newEmployee);
dbContext.SubmitChanges();
}
105 | P a g e
GetData();
}
GetData();
}
GetData();
}
}
}
106 | P a g e
GridView1.DataSource = linqQuery;
GridView1.DataBind();
}
GridView1.DataSource = linqQuery;
GridView1.DataBind();
}
Response.Write(dbContext.GetCommand(linqQuery).CommandText);
Response.Write("<br/>");
Response.Write(dbContext.GetCommand(linqQuery).CommandType);
GridView1.DataSource = linqQuery;
GridView1.DataBind();
}
Here are the steps for using stored procedure with LINQ to SQL
Step 1 : Create the stored procedure
Create procedure GetEmployees
as
Begin
Select ID, FirstName, LastName, Gender, Salary, DepartmentId
from Employees
End
Step 2 : In Visual Studio, click on the "View" menu and select "Server Explorer". Right click
107 | P a g e
on "Data Connection" and select "Refresh". Expand "Stored Procedures"folder. Here you
should find "GetEmployees" stored procedure.
Step 3 : Drag "GetEmployees" stored procedure from the Server Explorer window and drop it
on the LINQ to SQL class designer. This will automatically create a method with the same
name as the stored procedure. Tables and Views are converted to classes. Stored procedures
and functions are converted to methods.
Step 4 : Finally in the code-behind file, call GetEmployees() method on the DataContextclass
instance.
SampleDataContext dbContext = new SampleDataContext();
GridView1.DataSource = dbContext.GetEmployees();
GridView1.DataBind();
At this point, run SQL profiler and start a new trace. Run the application and click Get Data
button. In the SQL profiler trace notice that the stored procedure is called as expected.
108 | P a g e
There are 2 things that we need to understand here
1. GetEmployeesResult is an auto-generated type and follows the
"[StoredProcedureName]Result" naming pattern.
2. ISingleResult implies that we are getting back a single result set and not multiple result sets.
Another way to do this is, when dragging and dropping the stored procedure on the designer
surface, make sure to drop it on the Employee entity. Doing so will also set the return type
to ISingleResult<Employee>.
109 | P a g e
@ID int,
@FirstName nvarchar(50),
@LastName nvarchar(50),
@Gender nvarchar(50),
@Salary int,
@DepartmentId int
as
Begin
Update Employees Set
FirstName = @FirstName, LastName = @LastName, Gender = @Gender,
Salary = @Salary, DepartmentId = @DepartmentId
where ID = @ID
End
GO
Step 2 : In Visual Studio, click on the "View" menu and select "Server Explorer". Right click
on "Data Connection" and select "Refresh". Expand "Stored Procedures"folder. Here you
should find all the stored procedures.
Step 3 : Drag Insert, Update and Delete stored procedures from the Server Explorerwindow
and drop it on the LINQ to SQL class designer. This will automatically create respective
methods with the same name as the stored procedures.
110 | P a g e
Step 4 : Mapping stored procedures to work with LINQ to SQL
a) Right click on Employee entity on LINQ to SQL designer and select "Configure
Behavior" option.
b) In the "Configure Bevior" window, set
Class = Employee
Behavior = Insert
Select "Customize" radio button
From the DropDownList, select InsertEmployee() stored procedure
Map Method Arguments to Class properties
c) Finally click OK
d) In a similar fashion, configure stored procedures for Update and Delete operations.
At this point, run SQL profiler and start a new trace. Run the application and performInsert,
Update and Delete. In the SQL profiler trace notice that the respective stored procedures are
called as expected.
111 | P a g e
Part 38 - Stored procedures with output parameters in LINQ to SQL
In this video, we will discuss how to call stored procedures with output parameters using LINQ
to SQL. This is continuation to Part 37. Please watch Part 37 before proceeding.
End
Step 2 : In Visual Studio, click on the "View" menu and select "Server Explorer". Right click
on "Data Connection" and select "Refresh". Expand "Stored Procedures"folder. Here you
should find "GetEmployeesByDepartment" stored procedure.
112 | P a g e
Step 3 : Drag "GetEmployeesByDepartment" stored procedure from the Server
Explorer window and drop it on the LINQ to SQL class designer. This will automatically create
a method with the same name as the stored procedure.
Step 4 : Drag and drop a button and a label control on the webform.
Double click the button control to generate the click event handler method.
If you are following along the design of the webform, should be as shown below.
113 | P a g e
What is SqlMetal
SqlMetal is a command-line code generation tool used to generate LINQ-to-SQL classes.
There are 2 ways to generate LINQ-to-SQL classes
1. Uisng Visual Studio OR
2. Using SqlMetal
Step 2 : In C:\ create a new folder. Name it SqlMetalGeneratedFiles. This is the folder where
we will store the generated dbml file.
For the full list of all available options that can be used with SqlMetal.exe, please check the
following MSDN article
http://msdn.microsoft.com/en-gb/library/vstudio/bb386987(v=vs.100).aspx
Step 2 : In the web.config file, copy and paste the following connection string
<add name="SampleConnectionString"
connectionString="Data Source=venkat-pc;database=Sample;Integrated Security=True"
providerName="System.Data.SqlClient" />
Step 3 : Right click on the Demo project, in solution explorer and select Add - Existing
Item. Navigate to C:\SqlMetalGeneratedFiles and add Sample.dbml.
114 | P a g e
Step 4 : Add a WebForm to the project. Drag and drop a GridView control on
theWebForm. Copy and paste the following code in the code-behind file.
using System;
using System.Configuration;
namespace Demo
{
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string cs = ConfigurationManager.
ConnectionStrings["SampleConnectionString"].ConnectionString;
Example 1 :
Step 2 : We will be using Departments and Employees tables in this demo. Create the tables
using the following SQL script.
Create table Departments
(
ID int primary key identity,
Name nvarchar(50),
Location nvarchar(50)
)
GO
115 | P a g e
Salary int,
DepartmentId int foreign key references Departments(Id)
)
GO
Step 4 : Right click on the project in solution explorer and add LINQ to SQL Classes. Change
the name from DataClasses1.dbml to Sample.dbml
Step 5 : At this point, Sample.dbml file should have been added to the project. Click onServer
Explorer link on Sample.dbml file. In the Server Explorer window, you should find all the
tables in the Sample database. Drag and drop the tables on Sample.dbml file.
Step 6 : At this point we should have Department and Employee classes. The properties of the
class map to the columns of the respective table in the database. The arrow between the
classes represent the association between them. These associations are modeled based on the
primary-key/foreign-key relationships between the tables in the database. Notice that the arrow
is pointing from Department to Employee entity. In this case there is a One-to-Many relationship
between Department and Employee entities. A Department can have 1 or more employees.
116 | P a g e
Step 7 : Copy and paste the following code in the Main() method in Program.cs file.
using (SampleDataContext dbContext = new SampleDataContext())
{
//dbContext.Log = Console.Out;
Step 8 : The above code prints, Department Name and all the
employees FirstNameand LastName's belonging to that department. The output is shown
below.
117 | P a g e
relationship between Department andEmployee entities. A Department can have 1 or more
employees. When Departments are loaded the related entities (Employee entities) are not
loaded. Employee entities are only loaded when we iterate thru the employee objects belonging
to a given department.
To prove this, uncomment the following line and run the console application again. Notice that
there is one query to retrieve all the departments and 3 queries to retrieve the employees
belonging to each department.
//dbContext.Log = Console.Out;
Example 2 : Let us display Department Name and Employee Names in a GridView control as
shown below in an asp.net web application.
Step 2 : Name it Demo. Follow the steps in Example 1, from Step 4 to Step 6:
Step 3 : Add a WebForm to the project. Copy and paste the following HTML in the aspx page.
<asp:GridView ID="gvDepartments" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:BoundField HeaderText="Department" DataField="Name" />
<asp:TemplateField HeaderText="Employees">
<ItemTemplate>
<asp:GridView ID="gvEmployees" runat="server"
AutoGenerateColumns="false"
DataSource='<%# Eval("Employees") %>'>
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="First Name" />
<asp:BoundField DataField="LastName" HeaderText="Last Name" />
</Columns>
</asp:GridView>
</ItemTemplate>
</asp:TemplateField>
</Columns>
118 | P a g e
</asp:GridView>
Step 4 : Copy and paste the following code in the Page_Load() event of the code-behind file.
using (SampleDataContext dbContext = new SampleDataContext())
{
dbContext.Log = Response.Output;
gvDepartments.DataSource = dbContext.Departments;
gvDepartments.DataBind();
}
Run the application and notice that there is one query to retrieve all the departments and 3
queries to retrieve the employees belonging to each department. In this example we are data
binding both departments and employees.
In our next video, we will discuss how to change this behavior and eager load the related
entities.
119 | P a g e
foreach (Employee emp in dept.Employees)
{
Console.WriteLine("\t" + emp.FirstName + " " + emp.LastName);
}
}
}
Run the application, and notice that there is only one query which retrieves all the departments
and their related employee entities.
Again, run the application, and notice that there is only one query which retrieves all the
departments and their related employee entities.
Now let's do the same thing with the web application example we worked with in Part 8.
gvDepartments.DataSource = dbContext.Departments;
gvDepartments.DataBind();
}
120 | P a g e
dbContext.Log = Response.Output;
With lazy loading there is a problem called n + 1 select problem. Let us understand this
problem with an example. In this example there is a One-to-Many relationship between
Department and Employee entities. A Department can have 1 or more employees.
Now, let's say we need to iterate through all the Departments, and for each Department, we
want to print the list of the employees. By default, LINQ to SQL would do the following:
Select * from Departments
/* For each Department */
SELECT * FROM Employees WHERE DepartmentId = X
So, this means we have one select for the Departments, and then N additional selects to
retrieve the employees belonging to each Department, where N is the total number of
Departments. So, this is N + 1 problem.
What is the difference between eager loading and lazy loading? Which is good - eager
loading or lazy loading?
Without looking at the application architecture and what we are trying to achieve, we cannot say
one is better over the other. Both have their own advantages and disadvantages. There are
clear performance trade-offs between eager and lazy loading objects from a database.
With eager loading, all the data is retrieved in a single query, which can then be cached to
improve the application performance. With eager loading we are trading memory consumption
for database round trips.
121 | P a g e
With lazy loading, we only retrieve just the amount of data that we need in a single query.
When we need more data related to the initial data, additional queries are issued to the
database. This means there are several round trips between the application server and the
database server. In general, these database round trips are very often the major performance
bottleneck in most applications. Lesser the round trips, better the performance.
For example, if on a given page you are only displaying Departments, then there is no reason
for eager loading related Employees data. So in this case lazy loading works best. On the other
hand, if you are displaying both Department and Employees data, then eager loading works
best, as it avoids the additional round trips to the database.
If you are not sure of what data is exactly needed, start with lazy loading and if it is leading to N
+ 1 problem then eager load the data.
Insert into Employees values ('Mark', 'Male', 60000, NULL, NULL, 'PermanentEmployee')
Insert into Employees values ('Steve', 'Male', NULL, 50, 160, 'ContractEmployee')
Insert into Employees values ('Ben', 'Male', NULL, 40, 120, 'ContractEmployee')
Insert into Employees values ('Philip', 'Male', 45000, NULL, NULL, 'PermanentEmployee')
Insert into Employees values ('Mary', 'Female', 30000, NULL, NULL,'PermanentEmployee')
122 | P a g e
Insert into Employees values ('Valarie', 'Female', NULL, 30, 140, 'ContractEmployee')
Insert into Employees values ('John', 'Male', 80000, NULL, NULL, 'PermanentEmployee')
By default LINQ to SQL creates the following Employee entity class, based on Employees table
PermanentEmployee - This class should inherit from the abstract Employee class and should
contain AnnualSalary property
ContractEmployee - This class should inherit from the abstract Employee class and should
contain HourlyPay & HoursWorked properties
3. Right click on the designer surface and select Add - Class option. Change the class name
to ContractEmployee.
4. Cut HourlyPay & HoursWorked properties from Employee class and paste them
inContractEmployee class.
5. Right click on PermanentEmployee and select Add - Inheritance option. In the "New
Inheritance" window select "Employee" class from "Select a base class" dropdownlist
and "PermanentEmployee" class from "Select a derived class" dropdownlist. This should
create an inheritance relationship between Employee and PermanentEmployeeclasses.
123 | P a g e
6. Along the same lines, Right click on ContractEmployee and select Add - Inheritanceoption.
In the "New Inheritance" window select "Employee" class from "Select a base
class" dropdownlist and "ContractEmployee" class from "Select a derived
class"dropdownlist. This should create an inheritance relationship
between Employee andContractEmployee classes. At this point, the three classes should be
as shown below.
8. Along the same lines, right click on the inheritance relationship arrow(that connects Employee
and ContractEmployee classes) and select "Properties". Set the properties of the inheritance
relationship as shown below.
124 | P a g e
9. Finally right click on Employee class and select properties. In the properties window
set Inheritance Modifier = abstract. This should make Employee class an abstract class.
Now, let us see how to query the data. Design a webform as shown below.
namespace Demo
{
public partial class WebForm1 : System.Web.UI.Page
125 | P a g e
{
protected void Page_Load(object sender, EventArgs e)
{}
if (employee is PermanentEmployee)
{
dr["AnuualSalary"] = ((PermanentEmployee)employee).AnuualSalary;
dr["Type"] = "Permanent";
}
else
{
dr["HourlyPay"] = ((ContractEmployee)employee).HourlyPay;
dr["HoursWorked"] = ((ContractEmployee)employee).HoursWorked;
dr["Type"] = "Contract";
}
dt.Rows.Add(dr);
}
return dt;
}
switch (RadioButtonList1.SelectedValue)
{
case "Permanent":
GridView1.DataSource =
dbContext.Employees.OfType<PermanentEmployee>().ToList();
GridView1.DataBind();
break;
126 | P a g e
case "Contract":
GridView1.DataSource =
dbContext.Employees.OfType<ContractEmployee>().ToList();
GridView1.DataBind();
break;
default:
GridView1.DataSource =
ConvertEmployeesForDisplay(dbContext.Employees.ToList());
GridView1.DataBind();
break;
}
}
}
}
Run the application and inspect the queries that are generated.
Drag and drop a button control on the webform and set the following properties
ID="btnAddEmployees"
Text="Add Employees"
Double click on the button control to generate the click event handler. Copy and paste the
following code in the code-behind file.
protected void btnAddEmployees_Click(object sender, EventArgs e)
127 | P a g e
{
using (SampleDataContext dbContext = new SampleDataContext())
{
PermanentEmployee permanentEmployee = new PermanentEmployee
{
Name = "Emma",
Gender = "Female",
AnuualSalary = 65000
};
dbContext.Employees.InsertOnSubmit(permanentEmployee);
dbContext.Employees.InsertOnSubmit(contractEmployee);
dbContext.SubmitChanges();
}
}
Please note:
1. Since Employee is an abstract class, an instance of this class cannot be created.
2. The Discriminator column in the database is updated depending on the type of the
employee object (PermanentEmployee or ContractEmployee) being instantiated.
128 | P a g e
The following Linq query retrieves a single student whose ID = 1.
When this LINQ query is issued, LINQ has to parse the expression tree and dynamically
generate the required T-SQL statements. This happens every time a LINQ query is issued.
This overhead can be removed by compiling the linq query.
After the Students table is created, add a new LINQ to SQL class to the console project. Drag
and drop Students table from Server Explorer onto LINQ to SQL class designerfile.
129 | P a g e
Part 46 - How to directly execute sql queries using Linq to SQL
So far in this video series, we have not written any sql queries to retrieve data using linq to sql.
We write a linq query, and the underlying LINQ to SQL provider dynamically generates the T-
SQL required. If we want to have complete over the SQL that is executed against the database,
then we can use ExecuteQuery() or ExecuteCommand() methods of theDataContext class.
Step 3 : Add a new LINQ to SQL class to the console project. Drag and drop Studentstable
from Server Explorer onto LINQ to SQL class designer file.
Step 4 :
The following query retrieves all Male students from Students table
Select * from Students where Gender='Male'
Use the following code to execute the above query using DataContext object's ExecuteQuery()
method
using (SampleDataContext dbContext = new SampleDataContext())
{
IEnumerable<Student> students = dbContext.ExecuteQuery<Student>(
"Select * from Students where Gender='Male'");
130 | P a g e
In the above example, we have hard-coded Gender. If you want to parameterize the query, then
use the following syntax.
using (SampleDataContext dbContext = new SampleDataContext())
{
IEnumerable<Student> students = dbContext.ExecuteQuery<Student>(
"Select * from Students where Gender={0}", "Male");
If you want to perform an Insert, Update or Delete then use ExecuteCommand()method. This
method returns the number of rows affected by the query. The following code updates all 4 male
student's gender to Female.
using (SampleDataContext dbContext = new SampleDataContext())
{
int count = dbContext.ExecuteCommand(
"Update Students set Gender='Female' where Gender='Male'");
131 | P a g e
References:
http://msdn.microsoft.com/en-us/library/vstudio/dd627203(v=vs.100).aspx
In this example, we are retrieving two students from the database with the same identity and
using the the same datacontext object. With the first LINQ query is executed, it gets translated
to T-SQL. The SQL query is executed and the result is brought back into the application, where
a Student object is created. The object identity is stored in the Identity cache. When the second
LINQ query is issued for the same student object, LINQ checks the identity cache and returns a
reference to the student object that already exists. Notice that there is only one call to the
database, inspite of having 2 linq queries. S1 and S2 are pointing to the same student object in
memory.
using (SampleDataContext dbContext = new SampleDataContext())
{
dbContext.Log = Console.Out;
Output:
Each instance of LINQ to SQL DataContext class has its own identity cache. This means if
we have 2 different DataContext instances, and when we issue 2 linq queries 2 retrieve a
student with the same identity, we get 2 different student objects back. S1 and S2 are two
different objects in memory and registered in two different identity maps. Notice that the
database also gets called 2 times.
132 | P a g e
{
dbContext1.Log = Console.Out;
dbContext2.Log = Console.Out;
Output :
In this example we changed the FirstName of the student using the first datacontext object. We
then called SubmitChanges() method, so the database has stored the new name for this
student. However the student object for datacontext 2 still has the old name, because when we
reissue the linq query for the same student object, this object is retrieved from the identity cache
and not from the database. To have data retrieved from the database and to refresh the cache
with updated values, we need to call the Refresh method of the second datacontext object.
133 | P a g e
S1.FirstName = "Updated.....";
dbContext1.SubmitChanges();
Console.WriteLine("FirstName updated.....");
dbContext2.Refresh(System.Data.Linq.RefreshMode.OverwriteCurrentValues, S2);
Console.WriteLine("After calling the refresh method");
134 | P a g e
Output :
135 | P a g e
136 | P a g e
Part 48 - Difference between optimistic and pessimistic concurrency control
In this video we will discuss
1. Why is concurrency control required
2. Difference between optimistic and pessimistic concurrency
Let us understand what can happen if there is no concurrency control in place with an
example.
John and Mary has a joint account. At the moment the balance in the account is $1000. John
and Mary visits different branches. John wants to deposit $500 and Mary wants to withdraw
$500.
The following are the transactions at the bank branches without concurrency control
At the end of both the transaction the account balance must be $1000 (1000 - 500 + 500), but
the balance now is $1500 which is incorrect. This happened because 2 users updated the
balance at the same time and since there is no concurrency control in place, the second update
has overwritten the changes made by the first update. This is a concurrency related problem
and is called as Lost updates problem. There are several other concurrency related problems
137 | P a g e
which we will discuss in a later video session.
With the same example, let us now understand what can happen if there is some sort of
concurrency control in place.
The following are the transactions at the bank branches with concurrency control in place
In this example, the account is locked while John is processing his transaction. The lock is
released only after John's transaction is finished. After the lock has been released, Mary can
proceed with her transaction. Since we have a concurrency control in place, we prevented 2
users from updating the balance at the same time which also prevented lost updates. So, the
balance is updated correctly as expected.
138 | P a g e
able to access that data. Pessimistic locking can very easily lead to performance bottle necks in
an application.
Optimistic concurrency does not involve locking rows when reading. Instead, this model
checks if two users tried to update the same record at the same time. If that happens one user's
changes are committed and the other user's changes are discarded and an exception will be
thrown to notify the user.
We will discuss how Linq to SQL implements optimistic concurrency with an example in our
next video.
In this video we will discuss how linq to sql handles concurrent updates i.e when 2 or more
users try to update the same data at the same time.
Let us look at this in action with an example. We will be using the following Accounts table in
this demo.
Step 2 : Create a new empty asp.net web application project. Name it Demo. Add a new Linq to
SQL Class. Name it Sample. Drag and drop Accounts table on Sample.dbml file.
139 | P a g e
Here is the HTML for the web form.
<div style="font-family:Arial">
<table border="1">
<tr>
<td>
<b>Account Number</b>
</td>
<td>
<asp:Label ID="lblAccountNumber" runat="server"></asp:Label>
</td>
</tr>
<tr>
<td>
<b>Account Name</b>
</td>
<td>
<asp:Label ID="lblAccountName" runat="server"></asp:Label>
</td>
</tr>
<tr>
<td>
<b>Account Balance</b>
</td>
<td>
<asp:Label ID="lblAccountBalance" runat="server"></asp:Label>
</td>
</tr>
</table>
<br />
<asp:Button ID="btnDeposit" runat="server" Text="Deposit $500"
onclick="btnDeposit_Click" />
</div>
namespace Demo
{
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
140 | P a g e
{
if (!IsPostBack)
{
GetAccountData();
}
}
GetAccountData();
}
}
}
}
At this point open SQL Profiler and run a new trace. Run the web application. Click"Deposit
$500" button. Notice that the balance is updated to 1500 as expected.
To update the balance of the account all we need is the New Balance and
theAccountNumber that needs to be updated. In the WHERE clause of the query notice that
along with AccountNumber and the New Balance, we also have AccountName and
theoriginal AccountBalance. The reason we have these is to make sure that no column values
in the row have changed since we have loaded the data from the database. If any of the values
have changed, then the update fails and an exception will be thrown.
Let's now simulate the scenario of 2 users updating the same record at the same time. To
141 | P a g e
do this
Step 2 : Run the application in Debug mode, and click "Deposit $500" button. The processing
should stop just before we call SubmitChanges() method.
Step 3 : At this point open SQL Server Management Studio, and execute the following query
Update Accounts set AccountBalance = AccountBalance - 500
Where AccountNumber = 1
Step 4 : Now come back to Visual Studio and press F5 to continue the execution. Notice
that "ChangeConflictException" is thrown and the exception message states - Row not found
or changed.
This is continuation to Part 49. Please watch Part 49 before proceeding. In this video we will
discuss how to handle ChangeConflictException.
KeepCurrentValues - Keeps all the current changes made by the current user in the
DataContext object. SubmitChanges() method will save all changes made by the current user,
overwriting any changes made by other users after the data was loaded by the current user.
KeepChanges - Keeps the current values that have been changed, but updates the other
values with the database values. SubmitChanges() method will save any changes made by the
current user and will preserve any changes made by other users. If another user changed the
same value as the current user, the current user's change will overwrite it.
OverwriteCurrentValues - Updates the DataContext with the current database values, which
means that all changes made by the current user will be discarded.
Example : To handle the exception include the Linq to Sql code in try/catch block. Modify the
code in btnDeposit_Click() as shown below.
142 | P a g e
db.SubmitChanges();
GetAccountData();
}
catch (ChangeConflictException)
{
db.ChangeConflicts.ResolveAll(RefreshMode.OverwriteCurrentValues);
db.SubmitChanges();
GetAccountData();
}
}
}
Step 2. Run the application in Debug mode, and click "Deposit $500" button. The processing
should stop just before we call SubmitChanges() method.
Step 3 : At this point open SQL Server Management Studio, and execute the following query
Update Accounts set AccountBalance = AccountBalance - 300
Where AccountNumber = 1
Step 4 : Now come back to Visual Studio and press F5 to continue the execution. Notice
that "ChangeConflictException" is thrown and handled. Also, the balance gets overwritten
with the current value.
Run the application again in Debug mode, and click "Deposit $500" button. When the
application excution stops at the break point. Execute the following UPDATE statement from
143 | P a g e
SQL Server Management Studio. The SQL statement has updated 2 columns (AccountName
and AccountBalance)
Now come back to Visual Studio and press F5 to continue the execution. Notice that
"ChangeConflictException" is thrown and handled. The balance gets overwritten with the current
value, but the AccountName is not. This is because RefreshMode.KeepChanges option will
keep and update only the values that have been changed by the current user since the data is
loaded into the DataContext. This means SubmitChanges() will save only changes made by the
current user and will preserve any changes made by other users.
Run the application again in Debug mode, and click "Deposit $500" button. When the
application excution stops at the break point. Execute the following UPDATE statement from
SQL Server Management Studio. The SQL statement has changed AccountName and
AccountBalance.
Now come back to Visual Studio and press F5 to continue the execution. Notice that
"ChangeConflictException" is thrown and handled. Notice that the changes made by the other
user are preserved. So, RefreshMode.OverwriteCurrentValues will update the DataContext with
the current database values, which means that all changes made by the current user will be
discarded.
144 | P a g e
This property can be set to one of the 3 values of the UpdateCheck enum. This enum is
present in System.Data.Linq.Mapping namespace. The following are the different values of
UpdateCheck enum and what they mean.
The default is Always. This is means by default all the columns will be used to detect
concurrency conflicts.
Example :
1. Open Sample.dbml file and right click on "AccountBalance" property and select "Properties"
option from the context menu.
2. In the properties window set, UpdateCheck = Never. At this point open, Sample.Designer.cs
file and notice that the AccountBalance property has UpdateCheck property applied.
[global::System.Data.Linq.Mapping.ColumnAttribute
(Storage="_AccountBalance", DbType="Int", UpdateCheck=UpdateCheck.Never)]
public System.Nullable<int> AccountBalance
{
get
145 | P a g e
{
return this._AccountBalance;
}
set
{
if ((this._AccountBalance != value))
{
this.OnAccountBalanceChanging(value);
this.SendPropertyChanging();
this._AccountBalance = value;
this.SendPropertyChanged("AccountBalance");
this.OnAccountBalanceChanged();
}
}
}
Notice, that AccountBalance is removed from the WHERE clause, which means this column is
not used for detecting concurrency conflicts.
This is continuation to Part 51. Please watch Part 51 before proceeding. In this video we will
discuss how to use ROWVERSION or TIMESTAMP columns to detect concurrency conflicts in
linq to sql. Let us understand this with an example. We will be using the
following Accounts table in this demo.
By default LINQ to SQL uses all the columns of the table in the WHERE clause to detect
concurrency conflicts. The query would look as shown below.
exec sp_executesql N'UPDATE [dbo].[Accounts]
SET [AccountBalance] = @p3
146 | P a g e
WHERE ([AccountNumber] = @p0) AND ([AccountName] = @p1)
AND ([AccountBalance] = @p2)',
N'@p0 int,@p1 nvarchar(4000),@p2 int,@p3 int',@p0=1,
@p1=N'John Mary',@p2=1000,@p3=1500
This is OK if we have a few columns in the table. In real time applications we may have tables
with large number of columns. For example, what if the table has 30 columns. The WHERE
clause would be huge and it can impact the performance of the application.
In situations like this we can use ROWVERSION or TIMESTAMP columns. Here are the steps
Step 1 : Add a Version column to the Accounts table. The datatype of the column must be
either ROWVERSION or TIMESTAMP. The value for this column is automatically generated by
the database if the row gets changed. So this column can alone be used to detect concurrency
conflicts.
ALTER TABLE Accounts
ADD [Version] ROWVERSION
Step 2 : In Visual Studio, delete the Account Entity from the Sample.dbml file
Step 3 : In Server Explorer window in Visual Studio, right click on Accounts table and
select "Refresh".
Step 4 : Drag and drop Accounts table on the Designer surface of Sample.dbml file. Notice
that a Version Property is automatically added. Navigate to Sample.Designer.csfile and look at
the code generated for this property. Notice that IsVersion &IsDbGenerated properties are set
to true.
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "_Version",
AutoSync = AutoSync.Always, DbType = "rowversion NOT NULL", CanBeNull = false,
IsDbGenerated = true, IsVersion = true, UpdateCheck = UpdateCheck.Never)]
public System.Data.Linq.Binary Version
{
get
{
return this._Version;
}
set
{
if ((this._Version != value))
{
this.OnVersionChanging(value);
this.SendPropertyChanging();
this._Version = value;
this.SendPropertyChanged("Version");
this.OnVersionChanged();
}
}
}
Testing for concurrency conflicts : Let's now simulate the scenario of 2 users updating the
147 | P a g e
same record at the same time. To do this
Step 2 : Run the application in Debug mode, and click "Deposit $500" button. The execution
should stop on SubmitChanges() method.
Step 5 : Now come back to Visual Studio and press F5 to continue the execution. Notice
that "ChangeConflictException" is thrown as expected.
Step 6 : In SQL Profiler notice that the UPDATE query that is generated has used Version
column in the WHERE clause to detect concurrency conflicts.
exec sp_executesql N'UPDATE [dbo].[Accounts]
SET [AccountBalance] = @p2
WHERE ([AccountNumber] = @p0) AND ([Version] = @p1)
SELECT [t1].[Version]
FROM [dbo].[Accounts] AS [t1]
WHERE ((@@ROWCOUNT) > 0) AND ([t1].[AccountNumber] = @p3)',
N'@p0 int,@p1 timestamp,@p2 int,@p3 int',
@p0=1,@p1=0x0000000000002715,@p2=1500,@p3=1
Let us now discuss, creating an an XML tree in a single statement. We want to create an
XML tree that looks as shown below.
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
148 | P a g e
<!--Creating an XML Tree using LINQ to XML-->
<Students>
<Student Id="101">
<Name>Mark</Name>
<Gender>Male</Gender>
<TotalMarks>800</TotalMarks>
</Student>
<Student Id="102">
<Name>Rosy</Name>
<Gender>Female</Gender>
<TotalMarks>900</TotalMarks>
</Student>
<Student Id="103">
<Name>Pam</Name>
<Gender>Female</Gender>
<TotalMarks>850</TotalMarks>
</Student>
<Student Id="103">
<Name>John</Name>
<Gender>Male</Gender>
<TotalMarks>950</TotalMarks>
</Student>
</Students>
149 | P a g e
Code to create the XML Document
using System.Xml.Linq;
namespace Demo
{
class Program
{
public static void Main()
{
XDocument xmlDocument = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("Students",
150 | P a g e
new XElement("Name", "Mark"),
new XElement("Gender", "Male"),
new XElement("TotalMarks", 800)),
xmlDocument.Save(@"C:\Demo\Demo\Data.xml");
}
}
}
Upon running the console application, an XML file with name Data.xml should be created in the
respective project folder. To see the xml file, click on "Show All Files" icon in the solution
explorer. Double click on the xml file to open it in visual studio.
In Part 53, we discussed creating an XML document by hard-coding the XML elements in code.
In this video we will discuss, creating an XML document using in-memory collection of objects.
In real time applications we usually have the data residing in a database table. There are
many data access technologies available to retrieve data from the database. For example we
could use
1. Entity Framework or
2. LINQ to SQL or
3. ADO.NET
Once the data is retrieved from the database, the application may store the data in in-memory
data structures like arrays, list, queue etc. So, now let us discuss creating an XML document
from an array of Student objects. Here are the steps.
151 | P a g e
Step 2 : Add a class file to the project. Name it Student.cs. Copy and paste the following code.
namespace Demo
{
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
public int TotalMarks { get; set; }
return students;
}
}
}
152 | P a g e
using System.Xml.Linq;
namespace Demo
{
class Program
{
public static void Main()
{
XDocument xmlDocument = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("Students",
xmlDocument.Save(@"C:\Demo\Demo\Data.xml");
}
}
}
Upon running the console application, an XML file with name Data.xml should be created in the
respective project folder. To see the xml file, click on "Show All Files" icon in the solution
explorer. Double click on the xml file to open it in visual studio.
153 | P a g e
Part 55 - Querying xml document using linq to xml
In Parts 53 and 54 of LINQ to XML tutorial, we discussed creating XML documents using LINQ
to XML.
In this video, we will discuss how to query xml document using linq to xml. We want to
retrieve all the student names who has TotalMarks greater than 800. Students names should
be sorted by TotalMarks in descending order.
154 | P a g e
Output :
Alternate way of writing the above query. Change is highlighted in yellow color.
IEnumerable<string> names = from student in XDocument
.Load(@"C:\Demo\Demo\Data.xml")
.Element("Students")
.Elements("Student")
where (int)student.Element("TotalMarks") > 800
orderby (int)student.Element("TotalMarks") descending
select student.Element("Name").Value;
Inserting or adding new xml elements to the xml document : The following code adds the
student element at the end of the xml document.
xmlDocument.Element("Students").Add(
new XElement("Student", new XAttribute("Id", 105),
new XElement("Name", "Todd"),
new XElement("Gender", "Male"),
new XElement("TotalMarks", 980)
));
xmlDocument.Save(@"C:\Demo\Demo\Data.xml");
To add the xml element as the first element use AddFirst() method.
xmlDocument.Element("Students")
.Elements("Student")
.Where(x => x.Attribute("Id").Value == "103").FirstOrDefault()
155 | P a g e
.AddBeforeSelf(
new XElement("Student", new XAttribute("Id", 106),
new XElement("Name", "Todd"),
new XElement("Gender", "Male"),
new XElement("TotalMarks", 980)));
xmlDocument.Element("Students")
.Elements("Student")
.Where(x => x.Attribute("Id").Value == "106").FirstOrDefault()
.SetElementValue("TotalMarks", 999);
xmlDocument.Save(@"C:\Demo\Demo\Data.xml");
OR
xmlDocument.Element("Students")
.Elements("Student")
.Where(x => x.Attribute("Id").Value == "106")
.Select(x => x.Element("TotalMarks")).FirstOrDefault().SetValue(999);
xmlDocument.Save(@"C:\Demo\Demo\Data.xml");
xmlDocument.Save(@"C:\Demo\Demo\Data.xml");
xmlDocument.Save(@"C:\Demo\Demo\Data.xml");
The following code removes all "Student" elements that are present under root node
"Students"
XDocument xmlDocument = XDocument.Load(@"C:\Demo\Demo\Data.xml");
xmlDocument.Root.Elements().Remove();
156 | P a g e
xmlDocument.Save(@"C:\Demo\Demo\Data.xml");
In this video, we will discuss transforming the following XML document into CSV format.
<?xml version="1.0" encoding="utf-8"?>
<Students>
<Student Country="USA">
<Name>Mark</Name>
<Gender>Male</Gender>
<TotalMarks>800</TotalMarks>
</Student>
<Student Country="USA">
<Name>Rosy</Name>
<Gender>Female</Gender>
<TotalMarks>900</TotalMarks>
</Student>
<Student Country="India">
<Name>Pam</Name>
<Gender>Female</Gender>
<TotalMarks>850</TotalMarks>
</Student>
<Student Country="India">
<Name>John</Name>
<Gender>Male</Gender>
<TotalMarks>950</TotalMarks>
</Student>
</Students>
After transformation, data in the CSV file should look as shown below.
USA,Mark,Male,800
USA,Rosy,Female,900
India,Pam,Female,850
India,John,Male,950
157 | P a g e
XDocument.Load(@"C:\Demo\Demo\Data.xml").Descendants("Student")
.ToList().ForEach(element => sb.Append(
element.Attribute("Country").Value + delimiter +
element.Element("Name").Value + delimiter +
element.Element("Gender").Value + delimiter +
element.Element("TotalMarks").Value + "\r\n"));
After transformation, data in the HTML file should look as shown below.
<table border="1">
<thead>
<tr>
<th>Country</th>
<th>Name</th>
158 | P a g e
<th>Gender</th>
<th>TotalMarks</th>
</tr>
</thead>
<tbody>
<tr>
<td>USA</td>
<td>Mark</td>
<td>Male</td>
<td>800</td>
</tr>
<tr>
<td>USA</td>
<td>Rosy</td>
<td>Female</td>
<td>900</td>
</tr>
<tr>
<td>India</td>
<td>Pam</td>
<td>Female</td>
<td>850</td>
</tr>
<tr>
<td>India</td>
<td>John</td>
<td>Male</td>
<td>950</td>
</tr>
</tbody>
</table>
159 | P a g e
result.Save(@"C:\Demo\Demo\Result.htm");
Part 59 - Transform one XML format to another XML format using linq to xml
In Part 57, we discussed, tranforming XML to CSV
In Part 58, we discussed, tranforming XML to HTML
In this video, we will discuss transforming one XML format to another XML format
160 | P a g e
</USA>
<India>
<Student>
<Name>Pam</Name>
<Gender>Female</Gender>
<TotalMarks>850</TotalMarks>
</Student>
<Student>
<Name>John</Name>
<Gender>Male</Gender>
<TotalMarks>950</TotalMarks>
</Student>
</India>
</Students>
result.Save(@"C:\Demo\Demo\Result.xml");
In this video, we will discuss validating an XML file against an XSD (XML Schema Definition
Language) file.
161 | P a g e
Steps to validate an XML file using XSD file
Step 2 : Add a new XML Schema file to the project. Name it Student.xsd. Copy and paste the
following XML.
<?xml version="1.0" encoding="utf-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="Students">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Student" minOccurs="1" maxOccurs="4">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Name" minOccurs="1" maxOccurs="1"/>
<xsd:element name="Gender" minOccurs="1" maxOccurs="1"/>
<xsd:element name="TotalMarks" minOccurs="1" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
Step 3: Add a new XML file to the project. Name it Data.xml. Copy and paste the following
XML.
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Students>
<Student>
<Name>Mark</Name>
<Gender>Male</Gender>
<TotalMarks>800</TotalMarks>
</Student>
<Student>
<Name>Rosy</Name>
<Gender>Female</Gender>
<TotalMarks>900</TotalMarks>
</Student>
<Student>
<Name>Pam</Name>
<Gender>Female</Gender>
162 | P a g e
<TotalMarks>850</TotalMarks>
</Student>
<Student>
<Name>John</Name>
<Gender>Male</Gender>
<TotalMarks>950</TotalMarks>
</Student>
</Students>
Step 4 : To validate Data.xml against Student.xsd file, copy and paste the following code in
the Main() method.
XmlSchemaSet schema = new XmlSchemaSet();
schema.Add("", @"C:\Demo\Demo\Student.xsd");
if (validationErrors)
{
Console.WriteLine("Validation failed");
}
else
{
Console.WriteLine("Validation succeeded");
}
Step 5 : Run the application. Since the XML in Data.xml confirms to the XSD file, we get the
message Validation succeeded.
Step 6 : Remove <Name> element from one of the <Student> elements in Data.xml file. Run
the application again. Notice that the validation fails and we get the following error.
The element 'Student' has invalid child element 'TotalMarks'. List of possible elements
expected: 'Gender'.
Validation failed
163 | P a g e