Entity Framework
Entity Framework
What is LINQ?
LINQ (Language Integrated Query) is uniform query syntax in C# and VB.NET to
retrieve data from different sources and formats. It is integrated in C# or VB,
thereby eliminating the mismatch between programming languages and
databases, as well as providing a single querying interface for different types of
data sources.
For example, SQL is a Structured Query Language used to save and retrieve
data from a database. In the same way, LINQ is a structured query syntax built
in C# and VB.NET to retrieve data from different types of data sources such as
collections, ADO.Net DataSet, XML Docs, web service and MS SQL Server and
other databases.
LINQ Usage
The following example demonstrates a simple LINQ query that gets all strings
from an array which contains 'a'.
using System;
using System.Linq;
// LINQ Query
var myLinqQuery = from name in names
where name.Contains('a')
select name;
// Query execution
foreach (var name in myLinqQuery)
Console.Write(name + " ");
}
}
Let keyword
The 'let' keyword is useful in query syntax. It projects a new range variable,
allows re-use of the expression and makes the query more readable.
For example, you can compare string values and select the lowercase string
value as shown below:
select s
into teenStudents
where teenStudents.StudentName.StartsWith("B")
select teenStudents;
In the above query, the 'into' keyword introduced a new range variable
teenStudents, so the first range variable s goes out of scope. You can write a
further query after the into keyword using a new range variable.
The DbContext API in your applications is used as a bridge between your classes and your
database. The DbContext is one of the most important classes in the Entity Framework.
● It enables to express and execute queries.
● It takes query results from the database and transforms them into instances of our model
classes.
● It can keep track of changes to entities, including adding and deleting, and then triggers the
creation of insert, update and delete statements that are sent to the database on demand.
Following are the domain ad context classes on which we will be performing different operations
in this chapter. This is the same example which we have created in the chapater, Database First
Approach.
namespace DatabaseFirstDemo {
Standa
rd Query Operators in Method Syntax
Concatenation Concat
Equality SequenceEqual
Create Operation
Adding a new object with Entity Framework is as simple as constructing a new instance of your
object and registering it using the Add method on DbSet. The following code lets you add a new
student to the database.
class Program {
newStudent.FirstMidName = "Bill";
newStudent.LastName = "Gates";
newStudent.EnrollmentDate = DateTime.Parse("2015-10-21");
newStudent.ID = 100;
Update Operation
Changing existing objects is as simple as updating the value assigned to the property(s) you want
changed and calling SaveChanges. For example, the following code is used to change the last
name of Ali from Khan to Aslam.
using (var context = new UniContextEntities()) {
Delete Operation
To delete an entity using Entity Framework, you use the Remove method on DbSet. Remove
works for both existing and newly added entities. Calling Remove on an entity that has been
added but not yet saved to the database will cancel the addition of the entity. The entity is
removed from the change tracker and is no longer tracked by the DbContext. Calling Remove on
an existing entity that is being change-tracked will register the entity for deletion the next time
SaveChanges is called. The following example is of a code where the student is removed from
the database whose first name is Ali.
using (var context = new UniContextEntities()) {
var bay = (from d in context.Students where d.FirstMidName == "Ali"
select d).Single();
context.Students.Remove(bay);
context.SaveChanges();
}
Read Operation
Reading the existing data from the database is very simple. Following is the code in which all the
data from the Student table are retrieved and then a program will be displayed with the students’
first and last name in alphabetical order.
using (var db = new UniContextEntities()) {
var query = from b in db.Students orderby b.FirstMidName select b;
Console.WriteLine("All All student in the database:");
The context class (derived from DbContext) must include the DbSet type properties
for the entities which map to database tables and views.
We will be learning how to migrate changes into the database when there are multiple DbContext
classes in the application.
● Multiple DbContext was first introduced in Entity Framework 6.0.
● Multiple context classes may belong to a single database or two different databases.
In our example, we will define two Context classes for the same database. In the following code,
there are two DbContext classes for Student and Teacher.
Let’s take a look into the following example where multiple operations are performed
in a single transaction. The code is as −
class Program {
try {
context.Students.Add(student);
context.SaveChanges();
var query1 = context.Students.Where(s ⇒ s.ID == 200);
}
}
}
}
● Beginning a transaction requires that the underlying store connection is open.
● So calling Database.BeginTransaction() will open the connection, if it is not already opened.
● If DbContextTransaction opened the connection then it will close it when Dispose() is
called.
let us learn how to map Table-valued Functions (TVFs) using the Entity Framework Designer and
how to call a TVF from a LINQ query.
●TVFs are currently only supported in the Database First workflow.
●It was first introduced in Entity Framework version 5.
●To use the TVFs you must target .NET Framework 4.5 or above.
●It is very similar to stored procedures but with one key difference, i.e., the result of a TVF is
composable. This means the results from a TVF can be used in a LINQ query while the
results of a stored procedure cannot.
class Program {
Console.ReadKey();
}
}
}
Let's see how to execute asynchronous queries first and then, we will see an
asynchronous call to context.SaveChanges.
Asynchronous Query
The following is an example of the async method which executes a LINQ-to-Entity
query asynchronously and returns the result.
Console.WriteLine("Finished GetStudent...");
}
return student;
}
As you can see in the above code, the GetStudent() method is marked with the
async keyword, which makes it an asynchronous method. The return type of the
asynchrounous method must be Task<T>. The GetStudent() method returns an
object of the Student entity, so return type must be of Task<Student> type.
Also, the LINQ query is marked with the await keyword. This frees up the calling
thread to execute other code until it executes the query and returns the result. We
have used the FirstOrDefaultAsync async extension method to get the result. You
may use other async methods appropriately such as SingleOrDefaultAsync,
ToListAsyn etc.
Asynchronous Save
EF API provides the SaveChangesAsync() method to save entities to the database
asynchronously. The following SaveStudent method saves the Student entity to the
database asynchronously.
Console.WriteLine("Start SaveStudent...");
Console.WriteLine("Finished SaveStudent...");
}
}
query.Wait();
student.FirstName = "Steve";
studentSave.Wait();
Output
Start GetStudent...
Finished GetStudent...
Start SaveStudent...
Finished SaveStudent...
Saved Entities: 1
In the above example, the async method GetStudent() is called and it stores the
reference in the query variable. This will start to execute the GetStudent() method,
but frees the calling thread, so that it can execute further statements in the
AsyncQueryAndSave method. The query.wait() method holds the execution until the
asynchronous method completes. Once it completes, we can get the result using the
variable query.Result. In the same way, the asynchronous save method is called and
gets the result.
Adding or removing entities using the AddRange and RemoveRange methods improves
the performance. It is recommended to use these methods if you want to insert or
delete large number of records from the database using Entity Framework.
Linq-to-Entities Query
Here, you will learn how to write LINQ-to-Entities queries and get the result in Entity
Framework 6.x as well as in Entity Framework Core. Visit LINQ Tutorials to learn LINQ
step by step.
The DbSet class is derived from IQuerayable. So, we can use LINQ for querying
against DbSet, which will be converted to an SQL query. EF API executes this SQL
query to the underlying database, gets the flat result set, converts it into appropriate
entity objects and returns it as a query result.
The following are some of the standard query operators (or extension methods) that
can be used with LINQ-to-Entities queries.
First()
FirstOrDefault()
Single()
SingleOrDefault()
ToList()
Count()
Min()
Max()
Last()
LastOrDefault()
Average()
Find()
In addition to LINQ extension methods, we can use the Find() method of DbSet to
search the entity based on the primary key value.
Let's assume that SchoolDbEntities is our DbContext class and Students is the
DbSet property.
SELECT
[Extent1].[StudentID] AS [StudentID],
[Extent1].[StudentName] AS [StudentName],
[Extent1].[StandardId] AS [StandardId]
FROM [dbo].[Student] AS [Extent1]
WHERE [Extent1].[StudentId] = @p0',N'@p0 int',@p0=1
go
First/FirstOrDefault
If you want to get a single student object, when there are many students, whose
name is "Bill" in the database, then use First or FirstOrDefault, as shown below:
EF 6 executes the following SQL query in the database for the above LINQ query.
Parameterized Query
EF builds and executes a parameterized query in the database if the LINQ-to-Entities
query uses parameters, such as below.
The above query will result into the following SQL query in EF 6.
The difference between First and FirstOrDefault is that First() will throw an
exception if there is no result data for the supplied criteria, whereas
FirstOrDefault() returns a default value (null) if there is no result data.
ToList
The ToList method returns the collection result. If you want to list all the students
with the same name then use ToList():
We may also use ToArray, ToDictionary or ToLookup. The above query would result in
the following database query:
SELECT
[Extent1].[StudentID] AS [StudentID],
[Extent1].[StudentName] AS [StudentName],
[Extent1].[StandardId] AS [StandardId]
FROM [dbo].[Student] AS [Extent1]
WHERE 'Bill' = [Extent1].[StudentName]
go
GroupBy
Use the group by operator or GroupBy extension method to get the result based on
the group by the particular property of an entity.
The following example gets the results grouped by each Standard. Use the foreach
loop to iterate the group.
}
}
}
}
SELECT
[Project2].[C1] AS [C1],
[Project2].[StandardId] AS [StandardId],
[Project2].[C2] AS [C2],
[Project2].[StudentID] AS [StudentID],
[Project2].[StudentName] AS [StudentName],
[Project2].[StandardId1] AS [StandardId1]
FROM ( SELECT
[Distinct1].[StandardId] AS [StandardId],
1 AS [C1],
[Extent2].[StudentID] AS [StudentID],
[Extent2].[StudentName] AS [StudentName],
[Extent2].[StandardId] AS [StandardId1],
CASE WHEN ([Extent2].[StudentID] IS NULL) THEN CAST(NULL AS int) ELSE 1
END AS [C2]
FROM (SELECT DISTINCT
[Extent1].[StandardId] AS [StandardId]
FROM [dbo].[Student] AS [Extent1] ) AS [Distinct1]
LEFT OUTER JOIN [dbo].[Student] AS [Extent2] ON ([Distinct1].
[StandardId] = [Extent2].[StandardId]) OR (([Distinct1].[StandardId] IS
NULL) AND ([Extent2].[StandardId] IS NULL))
) AS [Project2]
ORDER BY [Project2].[StandardId] ASC, [Project2].[C2] ASC
go
OrderBy
Use the OrderBy operator with ascending/descending keywords in LINQ query syntax
to get the sorted entity list.
Use the OrderBy or OrderByDescending method to get the sorted entity list.
SELECT
[Extent1].[StudentID] AS [StudentID],
[Extent1].[StudentName] AS [StudentName],
[Extent1].[StandardId] AS [StandardId]
FROM [dbo].[Student] AS [Extent1]
ORDER BY [Extent1].[StudentName] ASC
go
ADVERTISEMENT
The following query returns a list of anonymous objects which contains StudentId and
StudentName properties.
SELECT
[s].[StudentID] AS [Id], [s].[StudentName] AS [Name]
FROM [Student] AS [s]
WHERE [s].[StandardId] = 1
go
The projectionResult in the above query will be the anonymous type, because there is
no class/entity which has these properties. So, the compiler will mark it as
anonymous.
Nested queries
You can also execute nested LINQ-to-entity queries as shown below:
The nested query shown above will result in an anonymous list with a StudentName
and Course object.
SELECT
[Extent1].[StudentID] AS [StudentID],
[Extent1].[StudentName] AS [StudentName],
[Join1].[CourseId1] AS [CourseId],
[Join1].[CourseName] AS [CourseName],
[Join1].[Location] AS [Location],
[Join1].[TeacherId] AS [TeacherId]
FROM [dbo].[Student] AS [Extent1]
INNER JOIN (SELECT [Extent2].[StudentId] AS [StudentId],
[Extent3].[CourseId] AS [CourseId1], [Extent3].[CourseName] AS
[CourseName],
[Extent3].[Location] AS [Location], [Extent3].[TeacherId] AS
[TeacherId]
FROM [dbo].[StudentCourse] AS [Extent2]
INNER JOIN [dbo].[Course] AS [Extent3]
ON [Extent3].[CourseId] = [Extent2].[CourseId] ) AS [Join1]
ON [Extent1].[StudentID] = [Join1].[StudentId]
WHERE 1 = [Extent1].[StandardId]
go
Sample Collections:
IList<Student> studentList = new List<Student>() { new Student()
{ StudentID = 1, StudentName = "John", Age = 18, StandardID = 1 } , new
Student() { StudentID = 2, StudentName = "Steve", Age = 21, StandardID =
1 } , new Student() { StudentID = 3, StudentName = "Bill", Age = 18,
StandardID = 2 } , new Student() { StudentID = 4, StudentName = "Ram" ,
Age = 20, StandardID = 2 } , new Student() { StudentID = 5, StudentName
= "Ron" , Age = 21 } }; IList<Standard> standardList = new
List<Standard>() { new Standard(){ StandardID = 1,
StandardName="Standard 1"}, new Standard(){ StandardID = 2,
StandardName="Standard 2"}, new Standard(){ StandardID = 3,
StandardName="Standard 3"} };
The following query returns Enumerable of anonymous object that has only
StudentName property:
Group By
The following query returns list students group by StandardID:
The output includes Ron who doesn't have any StandardID. So Ron falls under
StandardID 0.
In the following example of group by query, we sort the group and select only
StudentName:
Sorting
The following query returns list of students by ascending order of StandardID
and Age.
Example: Sorting
var sortedStudents = from s in studentList orderby s.StandardID, s.age
select new { StudentName = s.StudentName, Age = s.age, StandardID =
s.StandardID }; sortedStudents.ToList().ForEach(s =>
Console.WriteLine("Student Name: {0}, Age: {1}, StandardID: {2}",
s.StudentName, s.Age , s.StandardID));
Try it
Output:
Student Name: Ron, Age: 21, StandardID: 0
Student Name: John, Age: 18, StandardID: 1
Student Name: Steve, Age: 21, StandardID: 1
Student Name: Bill, Age: 18, StandardID: 2
Student Name: Ram, Age: 20, StandardID: 2
Inner Join
Example: LINQ Inner join - C#
var studentWithStandard = from s in studentList join stad in
standardList on s.StandardID equals stad.StandardID select new
{ StudentName = s.StudentName, StandardName = stad.StandardName };
studentWithStandard.ToList().ForEach(s => Console.WriteLine("{0} is in
{1}", s.StudentName, s.StandardName ));
Try it
Output:
John is in Standard 1
Steve is in Standard 1
Bill is in Standard 2
Ram is in Standard 2
Nested Query
C#:
var nestedQueries = from s in studentList where s.age > 18 &&
s.StandardID == (from std in standardList where std.StandardName ==
"Standard 1" select std.StandardID).FirstOrDefault() select s;
nestedQueries.ToList().ForEach(s => Console.WriteLine(s.StudentName));
Try it
Output:
Steve