entity framework tutorial part 02
TRANSCRIPT
www.entityframeworktutorial.net
1
Page 17: Querying Entity Graph with ObjectContext:
We will learn some important features of LINQ-to-Entities like Projection, Lazy loading & Eager
loading. Knowledge about LINQ is pre-requisites here.
Projection:
Projection is a process of selecting data in different shape rather than specific entity being
queried. There are many ways of projection. Let’s see some projection style:
If you want to get the single student object when there are many students whose name is
"Student1" in the database then use FirstOrDefault<>
var student = (from s in ctx.Students where s.StudentName == "Student1" select s).FirstOrDefault<Student>();
If you want to list of all students whose name is "Student1" (provided there are many students
has same name) then use ToList<>:
var studentList = (from s in ctx.Students where s.StudentName == "Student1" select s).ToList<Student>();
If you want to group students by standardId then use group:
var students = from s in ctx.Students group s by s.StandardId into studentsByStandard select studentsByStandard;
If you want to get the list of students sorted by StudentName then use OrderBy:
var student1 = from s in ctx.Students orderby s.StudentName ascending select s;
If you want to get only StudentName, StandardName and list of Courses for that student in
single object then write following projection:
var projectionResult = from s in ctx.Students where s.StudentName == "Student1" select new {
www.entityframeworktutorial.net
2
s.StudentName, s.Standard.StandardName, s.Courses };
Type of projectionResult in above query will be anonymous type because there is no class/entity
which has these properties. So compiler will mark it as anonymous.
So this way you can do projection of result the way you want data. There are different other
ways of projection but all projection styles requires knowledge of LINQ.
Page 18: Lazy Loading with ObjectContext:
Lazy loading means delaying the loading of related data until you specifically request it, for
example when you query for Student entity, you get all scalar and navigation properties of
Student:
var student1 = (from s in ctx.Students where s.StudentName == "Student1" select s).FirstOrDefault<Student>();
In the above query, student1 will have all the properties but student1.StudentAddress and
student1.Courses will be empty that means above query execute following SQL query in the
database:
SELECT [Extent1].[StudentID] AS [StudentID], [Extent1].[StudentName] AS [StudentName], [Extent1].[StandardId] AS [StandardId] FROM [dbo].[Student] AS [Extent1] WHERE N'Student1' = [Extent1].[StudentName]
As you can see in above SQL that it only gets StudentID, StudentName and StandardID from
the database. it doesn’t get StudentAddress and Courses from the database at first shot. This is
called Lazy Loading. It fetches the data in scalar & navigation property when you actually need
it.
So when you access student1.StudentAddress property, that time it will implicitly execute
following query and get the StudentAddress for "Student1":
www.entityframeworktutorial.net
3
SELECT [Extent1].[StudentID] AS [StudentID], [Extent1].[Address1] AS [Address1], [Extent1].[Address2] AS [Address2], [Extent1].[City] AS [City], [Extent1].[State] AS [State] FROM [dbo].[StudentAddress] AS [Extent1] WHERE [Extent1].[StudentID] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=234
So this way you can use lazy loading feature of entity framework to load the related data in
scalar and navigation properties only when you use it but not at first shot.
Page 19: Eager Loading with ObjectContext:
Eager loading is opposite of lazy loading. It loads the related data in scalar and navigation
properties along with query result at first shot. Let’s say we want to retrieve StudentAddress
along with Student entity using eager loading.
For eager loading, we have to use ‘Include’ method in the query. Include is a query builder
method and you can apply it to an ObjectQuery or ObjectSet type of EntitySet. Because Student
is an ObjectSet, we can use Include method in LINQ query as following:
var student1 = (from s in ctx.Students.Include("StudentAddress") where s.StudentName == "student1" select s).FirstOrDefault<Student>();
We have written ctx.Students.Include("StudentAddress"), so the result of above LINQ query will
include StudentAddress entity along with Student entity. So the SQL execute by above query
will join with StudentAddress table as following:
SELECT TOP (1) [Extent1].[StudentID] AS [StudentID], [Extent1].[StudentName] AS [StudentName], [Extent1].[StandardId] AS [StandardId], [Extent3].[StudentID] AS [StudentID1], [Extent3].[Address1] AS [Address1], [Extent3].[Address2] AS [Address2], [Extent3].[City] AS [City], [Extent3].[State] AS [State] FROM [dbo].[Student] AS [Extent1] LEFT OUTER JOIN
www.entityframeworktutorial.net
4
[dbo].[StudentAddress] AS [Extent2] ON [Extent1].[StudentID] = [Extent2].[StudentID] LEFT OUTER JOIN [dbo].[StudentAddress] AS [Extent3] ON [Extent2].[StudentID] = [Extent3].[StudentID] WHERE N'student1' = [Extent1].[StudentName]
So this way we can use Include() for eager loading.(If you have IObjectSet type of EntitySet
then you have to use extension method for Include(..))
For more information on LINQ-to-Entities: Visit MSDN to learn LINQ-to-Entities in detail. Visit MSDN’s Entity Framework Query Samples.
Page 20: Significance of SaveChanges:
SaveChanges method of ObjectContext is a gateway to persist all changes made to entities to
the database. When you call ObjectContext.SaveChanges(), it performs insert, update or delete
operation on the database based on EntityState of the entities.
Following code shows how you can persist modification made to the Student entities of
SchoolDB EDM created either with EntityObject entities or POCO Proxy entities.
//Update entity using SaveChanges method using (SchoolDBEntities ctx = new SchoolDBEntities()) { var stud = (from s in ctx.Students where s.StudentName == "Student1" select s).FirstOrDefault(); stud.StudentName = "Student2"; int num = ctx.SaveChanges(); }
As you can see in above code, we fetch the single Student entity whose name is “Student1” and
then we change the StudentName property to “Student2”. It saves this modification to the
database when we do ctx.SaveChanges(). This method also returns the number of rows
updated in the database.
www.entityframeworktutorial.net
5
SaveChanges also accepts SaveOptions parameter. SaveOption is an Enum which has three
values:
1. AcceptAllChangesAfterSave: After saving entities values to the database, context change entity states. Added and Modified entities become Unchanged and deleted entities are removed from the context.
2. DetectChangesBeforeSave: It tells context to detect changes before saving. 3. None: Neither AcceptAllChangesAfterSave or DetectChangesBeforeSave occurs
So this way SaveChanges method is the most important method in the EntityFramework.
Remember: SaveChanges method persist modifications made to all entities attached to it. So
for example in above code, if you fetch and modify ‘StudentAddress’ entity also and call
ctx.SaveChanges() then it will save modification of Student and StudentAddress entities to the
database.
Page 21: Persistence in Entity Framework
Now let’s see how we can persist an entity so that it either inserts new row or updates an
existing row or delete an existing row in the database.
We will see persisting an entity in two scenarios, connected scenario and disconnected
scenario.
Connected Scenario: Connected scenario is when an entity is retrieved from the database and
modified in the same context.
Disconnected Scenario: Disconnected scenario is when an entity is retrieved from the
database and modified in the different context. Disconnected scenario is complex because
context doesn’t know anything about modified entity so you have to tell to ObjectContext that
what has changed in entity.
So let's see how to save an entities to the database in next chapter..
www.entityframeworktutorial.net
6
Page 22: Save New Entity with ObjectContext:
We will use POCO Proxy entities generated by T4 template for SchoolDB database throughout
this tutorial.
Student student = new Student(); student.StudentName = "Student1"; using (SchoolDBContext ctx = new SchoolDBContext()) { ctx.Students.AddObject(student); ctx.SaveChanges(); }
As you can see in above code, we added new student entity to the context using
ctx.Students.AddObject(student) and then we called ctx.SaveChanges(), which will insert new
row in the Student table. So thus to persist any new entity as a new row in the database, just
add that entity into its EntitySet using AddObject() method and then call SaveChanges() of
context.
Above code will work in both connected and disconnected scenario to persist new entity as a
new row in the database.
Page 23: Update an Entity with ObjectContext:
Connected scenario: Now let’s update an existing student entity in connected scenario. So in
this scenario, we will fetch the student from database and persist modification to the database
using same context.
using (SchoolDBContext ctx = new SchoolDBContext()) { var stud = (from s in ctx.Students where s.StudentName == "Student1" select s).FirstOrDefault(); stud.StudentName = "Updated Student1"; int num = ctx.SaveChanges(); }
Above code shows how you can save modified entity to the database by simply calling
context.SaveChanges() method. We can do so because ObjectStateManager of the context
keeps tracks of current and original values of the student entity.
www.entityframeworktutorial.net
7
Disconnected scenario: Now let’s update an existing student in disconnected scenario. So in
this scenario, we will fetch the student from database and persist modification to the database
using different context.
Student stud = null; using (SchoolDBContext ctx = new SchoolDBContext()) { ctx.ContextOptions.LazyLoadingEnabled = false; stud = (from s in ctx.Students where s.StudentName == " student1" select s).FirstOrDefault(); } //Out of using scope so ctx has disposed here stud.StudentName = "Updated student1"; using (SchoolDBContext newCtx = new SchoolDBContext()) { newCtx.Students.Attach(stud); newCtx.ObjectStateManager.ChangeObjectState(stud, System.Data.EntityState.Modified); newCtx.SaveChanges(); }
As you can see in the above code, we fetch the student entity using 'ctx' context but we modify
student entity out of the scope of 'ctx' context. So 'ctx' context doesn’t track student entity
anymore. But when we save modified student entity, we use new context object "newCtx". So
here we have to attach modified student entity to the newCtx because we are modifying an
existing entity and then we have to pass the EntityState of the attached entity using state-
manager(newCtx.ObjectStateManager). And finally call SaveChanges method of "newCtx"
context. Thus you can update an entity in disconnected scenario.
Page 24: Delete an Entity with ObjectContext:
Now let’s see how to delete an entity in both the scenario.
Connected scenario:
using (SchoolDBContext ctx = new SchoolDBContext()) { var stud = (from s in ctx.Students where s.StudentName == "Student1"
www.entityframeworktutorial.net
8
select s).FirstOrDefault(); ctx.Students.DeleteObject(stud); int num = ctx.SaveChanges(); }
As you can see in the above code, we delete the student entity just by calling DeleteObject of
Students entityset. This will remove student entity from Students entityset. And then finally call
to SaveChanges will delete the student row from the student table in the database.
Disconnected scenario:
Student stud = null; using (SchoolDBContext ctx = new SchoolDBContext()) { stud = (from s in ctx.Students where s.StudentName == "Student1" select s).FirstOrDefault(); } using (SchoolDBContext newCtx = new SchoolDBContext()) { newCtx.Students.Attach(stud); newCtx.Students.DeleteObject(stud); //you can use ObjectStateManager also //newCtx.ObjectStateManager.ChangeObjectState(stud, System.Data.EntityState.Deleted); int num = newCtx.SaveChanges(); }
As you can see in the above code, we fetch the student entity using ‘ctx’ context but we delete it
using ‘newCtx’ context. So to delete the entity in disconnected scenario we have to attach
student entity into Students entityset and call DeleteObject method of Students entityset to
delete it. And finally call SaveChanges method of context object to delete it from student table in
the database.
Additionally, you can also mark attached entity as deleted using state-manager and then call the
SaveChanges(), which will delete the student from the database.
www.entityframeworktutorial.net
9
Page 25: Entity Graph:
When an entity has relation with other entities then it called entity graph because more entities
are involved, for example Student entity graph includes many other entities like Standard,
StudentAddress & Course.
An entity can have many types of relations with other entities in EDM as we have seen in Entity
relationships. It can be One-to-One, One-to-Many or Many-to-Many. We will see how we can
persist an entity graph which has One-to-One, One-to-Many and Many-to-Many relationships in
connected and disconnected scenario.
Page 26: Add One-to-One Entities with ObjectContext:
Here, we will see how to save an entities which has One-to-One relationships.
There will not be any difference in connected or disconnected scenario code to add an entity
graph as a new row. So we will see code example which will work in both the scenario.
Following code saves Student and StudentAddress entities as a new row in Student and
StudentAddress table in the database which has One-to-One relationship:
Student student = new Student(); student.StudentName = "Student2"; Standard std = new Standard(); std.StandardName = "Standard2"; student.Standard = std; StudentAddress sAddress = new StudentAddress(); sAddress.Address1 = "Address1"; sAddress.Address2 = "Address2"; sAddress.City = "City1"; sAddress.State = "State1"; student.StudentAddress = sAddress; using (SchoolDBContext ctx = new SchoolDBContext()) { ctx.Students.AddObject(student); ctx.SaveChanges(); }
www.entityframeworktutorial.net
10
As you can see in above code, we just add student object to Students EntitySet and then call
SaveChanges. This will automatically insert new row not only for Student but also for Standard
and StudentAddress table in the database. Because Standard and StudentAddress is assigned
to Student entity, context automatically detects it and insert it as a new row in respected table.
We don’t have to add Standard and StudentAddress entities into its respected EntitySet. Thus
you can add One-to-One entity graph easily.
Page 27: Add One-to-Many Entities with ObjectContext:
Following code saves one Standard and multiple teachers for that Standard as a one new row in
Standard table and three new rows in Teacher table in the database which has One-to-Many
relation:
There will not be any difference in connected or disconnected scenario code to add an entity
graph as a new row. So we will see code example which will work in both the scenario.
Following code saves Student and StudentAddress entities as a new row in Student and
StudentAddress table in the database which has One-to-One relationship:
Standard std = new Standard(); std.StandardName = "Standard1"; std.Description = "Demo standard"; Teacher teacher1 = new Teacher(); teacher1.TeacherName = "Teacher1"; Teacher teacher2 = new Teacher(); teacher2.TeacherName = "Teacher2"; Teacher teacher3 = new Teacher(); teacher3.TeacherName = "Teacher3"; //Adding many teachers for one standard std.Teachers.Add(teacher1); std.Teachers.Add(teacher2); std.Teachers.Add(teacher3); using (SchoolDBContext ctx = new SchoolDBContext()) { ctx.Standards.AddObject(std); ctx.SaveChanges(); }
www.entityframeworktutorial.net
11
As you can see in above code, we have created new Standard entity and because Standard
and Teacher entity has One-to-Many relationship, we have added three new teacher entities
into Standard using std.Teachers.Add(..). Then we just add standard entity to Standards
EntitySet and call SaveChanges. So this will insert new row in standard and three new rows in
teacher table associated with added standard in the database. Thus you can save One-to-Many
entity graph.
Page 28: Add Many-to-Many Entities with ObjectContext:
Following code saves one Student and multiple courses for that Student entity along with
Standard and Teacher. Student and Course has Many-to-Many relationship through
StudentCourse table in the database. So following code insert new row in Student table and
Course table and also insert new row in StudentCourse table which will include PK of newly
generated Student and Course.
Here we will only see how we can persist one Student and multiple courses for the student.
However, you can do vice-versa the same way.
Following code saves Student and StudentAddress entities as a new row in Student and
StudentAddress table in the database which has One-to-One relationship:
Student student = new Student(); student.StudentName = "student1"; Standard std = new Standard(); std.StandardName = "standard1"; std.Description = "Demo standard"; student.Standard = std; Course course1 = new Course(); course1.CourseName = "Course1 "; course1.Location = "City1"; Course course2 = new Course(); course2.CourseName = "Course2 "; course2.Location = "City2"; Course course3 = new Course(); course3.CourseName = "Course3 "; course3.Location = "City1";
www.entityframeworktutorial.net
12
Teacher teacher1 = new Teacher(); teacher1.TeacherName = "teacher1"; teacher1.Standard = std; //assign teacher1 for each courses course1.Teacher = teacher1; course2.Teacher = teacher1; course3.Teacher = teacher1; //Add courses to student student.Courses.Add(course1); student.Courses.Add(course2); student.Courses.Add(course3); using (SchoolDBContext ctx = new SchoolDBContext()) { //add whole student entity graph to context ctx.Students.AddObject(student); ctx.SaveChanges(); }
As you can see in above code that there is no difference in saving One-to-One or Many-to-Many
student entity graph. We have just added student entity to Students EntitySet and calling
SaveChanges. This will insert new row in student table, three new rows in course table and
three new rows in StudentCourse table which is joining table of Student and Course. So thus
you can save Many-to-Many entity graph easily in Entity Framework 4.x.
Page 29: Update One-to-One Entities with ObjectContext:
Now let’s see how we can update an entity graph which intern updates the changes in the
database in connected as well as disconnected scenario.
Connected Scenario:
Following code shows how we can save modified Student and StudentAddress entity graph to
the database in connected scenario:
using (var ctx = new SchoolDBContext()) { Student student = (from s in ctx.Students where s.StudentName == "Student1" select s).FirstOrDefault<Student>(); student.StudentName = "Updated Student1";
www.entityframeworktutorial.net
13
StudentAddress sAddress = student.StudentAddress; sAddress.Address1 = "Updated Address1"; sAddress.Address2 = "Updated Address2"; sAddress.City = "Updated City"; sAddress.State = "Updated State"; student.StudentAddress = sAddress; ctx.SaveChanges(); }
As you can see in above code that we fetch the student entity from database whose name is
"Student1" and then we modified it’s StudentName and other StudentAddress’s properties. To
update these changes in the database, we just call SaveChanges. So this will update all the
modified properties to the respected tables in the database. So the only call to SaveChanges
will update the database tables in connected scenario.
Disconnected Scenario:
Following code shows how we can update the Student and StudentAddress entity graph to the
database in disconnected scenario:
Student student = null; using (var ctx = new SchoolDBContext()) { ctx.ContextOptions.LazyLoadingEnabled = false; student = (from s in ctx.Students.Include("StudentAddress") where s.StudentName == "student2" select s).FirstOrDefault<Student>(); } student.StudentName = "Updated student2"; //update student address student.StudentAddress.Address1 = "Updated Address1"; student.StudentAddress.Address2 = "Updated Address2"; student.StudentAddress.City = "Updated City"; student.StudentAddress.State = "Updated State"; using (var newCtx = new SchoolDBContext()) {
www.entityframeworktutorial.net
14
newCtx.Students.Attach(student); //Mark student entity as Modified EntitySet newCtx.ObjectStateManager.ChangeObjectState(student, System.Data.EntityState.Modified); //Mark studentAddress entity as Modified EntitySet newCtx.ObjectStateManager.ChangeObjectState(student.StudentAddress, System.Data.EntityState.Modified); newCtx.SaveChanges(); }
As you can see in above code that we just modified properties of Student and StudentAddress
entities in disconnected mode. When we used new context object to finally update the database,
we first attached the student entity to Students EntitySet and then marked student and
studentAddress entity as Modified using ObjectStateManager and then calling SaveChanges.
These steps will update the Student and StudentAddress table in the database. so marking
each entity in entity graph as modified is necessary to update the respected table in the
database in disconnected scenario. In case, you don’t mark StudentAddress entity as modified
then context will not update the StudentAddress table. So don’t forget to mark entities as
modified in disconnected scenario.
Page 30: Update One-to-Many Entities with ObjectContext:
Now we will see how we can update the entity graph which has One-to-Many relationship in
connected and disconnected scenario.
Connected Scenario:
Following code shows how we can save modified Standard and Teachers entity graph which
has One-to-Many relationship to the database in connected scenario:
using (var ctx = new SchoolDBContext()) { //fetching existing standard from the db Standard std = (from s in ctx.Standards where s.StandardName == "standard3" select s).FirstOrDefault<Standard>(); std.StandardName = "Updated standard3";
www.entityframeworktutorial.net
15
std.Description = "Updated standard"; //getting first teacher to be removed Teacher tchr = std.Teachers.FirstOrDefault<Teacher>(); //removing teachers (enable cascading delete for the teacher) if (tchr != null) ctx.Teachers.DeleteObject(tchr); Teacher stdTeacher = std.Teachers.FirstOrDefault<Teacher>(); if (stdTeacher != null) stdTeacher.TeacherName = "Updated Teacher"; Teacher newTeacher = new Teacher(); newTeacher.TeacherName = "New Teacher"; std.Teachers.Add(newTeacher); Teacher existingTeacher = (from t in ctx.Teachers where t.StandardId != std.StandardId select t).FirstOrDefault<Teacher>(); if (existingTeacher != null) std.Teachers.Add(existingTeacher); ctx.SaveChanges(); }
As you can see in above code that there is no difference in update mechanism of one-to-many
entity graph in connected scenario. Just call to SaveChanges will update all respected tables for
the entities.
Disconnected Scenario:
Saving entity graph which has one-to-many relationship in disconnected scenario is much
complex process. We will see how we can save modified Standard and Teachers entity graph to
the database in disconnected scenario which has One-to-Many relationship. Let’s do it step by
step.
www.entityframeworktutorial.net
16
First, fetch the standard entity including its teachers. Following code fetch the Standard entity
which has StandardName as "standard1" including teachers using “Include” method in LINQ-to-
Entities from the database:
Standard std = null; using (var ctx = new SchoolDBContext()) { //fetching existing standard from the db std = (from s in ctx.Standards.Include("Teachers") where s.StandardName == "standard1" select s).FirstOrDefault<Standard>(); }
Following code modifies properties of Standard entity. It also modifies the first teacher, deletes
the second teacher and then adds new teacher in the Teachers collection of standard in
disconnected scenario (out of context scope):
std.StandardName = "Updated standard3"; std.Description = "Updated standard description"; if (std.Teachers != null) { if (std.Teachers.Count >= 2) { //get the first element to be updated Teacher updateTchr = std.Teachers.ElementAt<Teacher>(0); //get the second element to be removed Teacher deletedTchr = std.Teachers.ElementAt<Teacher>(1); //remove updated teacher to re-add later std.Teachers.Remove(updateTchr); //delete second teacher from the list // deleted second teacher std.Teachers.Remove(deletedTchr); //Update first teacher in the list updateTchr.TeacherName = "Updated Teacher1";
www.entityframeworktutorial.net
17
// re-add first teacher std.Teachers.Add(updateTchr); } } // adding new teacher for selected standard Teacher newTeacher = new Teacher(); newTeacher.TeacherName = "NewTeacher"; std.Teachers.Add(newTeacher);
Now, following code shows how we can update the standard entity graph using new context
where some teachers are updated, some are deleted and some teachers are added in the
Teachers collection for the standard entity:
//Save standard and tearchers Updated above in different context just to create disconnected scenario using (var newCtx = new SchoolDBContext()) { //fetch existing standard info var exitingStandard = (from s in newCtx.Standards where s.StandardId == std.StandardId select s).FirstOrDefault<Standard>(); var newTeachers = std.Teachers.ToList<Teacher>(); var existingTeachers = exitingStandard.Teachers.ToList<Teacher>(); // find added teachers from newTeachers whose TeacherId doesn't match with Existing teachers var addedTeachers = newTeachers.Except(existingTeachers, tchr => tchr.TeacherId); // find deleted teachers from existing (from db) teachers whose TeacherId is not in newTeachers list var deletedTeachers = exitingStandard.Teachers.Except(newTeachers, tchr => tchr.TeacherId); //find Updated teachers from existing teachers which is either not deleted or Added
www.entityframeworktutorial.net
18
var updatedTeachers = exitingStandard.Teachers.Except(deletedTeachers, tchr => tchr.TeacherId); //Add new teachers to context addedTeachers.ToList<Teacher>().ForEach(t => newCtx.Teachers.AddObject(t)); //Attacher Updated teachers to context and mark it's state as Modified foreach (Teacher t in updatedTeachers) { newCtx.Teachers.Attach(t); newCtx.ObjectStateManager.ChangeObjectState(t, System.Data.EntityState.Modified); } // delete the deleted teachers in the context deletedTeachers.ToList<Teacher>().ForEach(t => newCtx.Teachers.DeleteObject(t)); // save all above changes newCtx.SaveChanges(); }
As you can see in above code that first we re-fetch existing standard from the database using
new context object. Then we find the added teachers using “Except” method of teachers
collection by matching teacherId of new collection of teachers and existing collection of
teachers. E.g.var addedTeachers = newTeachers.Except(existingTeachers, tchr =>
tchr.TeacherId);
Above statement will return list of teachers whose TeacherId doesn’t match with existing
teacher’s TeacherId. So here, all the new teachers whose TeacherId is zero will be returned
because it hasn’t been generated yet.
So the same way we find the updated teachers and deleted teachers from the Teachers
collection. Then we add, update or delete the respected teachers in new context and then call
SaveChanges.
So thus we have to find added, updated and deleted entities from the collection and do CUD
operation in disconnected scenario.
www.entityframeworktutorial.net
19
Page 31: Update Many-to-Many Entities with ObjectContext:
Now let’s see how to save student and course entity graph which has Many-to-Many relation to
the database.
Connected Scenario:
Following code saves modified Student and Courses (for that student) to the database:
using (var ctx = new SchoolDBContext()) { Student student = (from s in ctx.Students where s.StudentName == "Student3"s select s).FirstOrDefault<Student>(); student.StudentName = "Updated Student3"; Course cours = student.Courses.FirstOrDefault<Course>(); //removing course from student student.Courses.Remove(cours); ctx.SaveChanges(); }
As you can see in above code, we get the first course from Student's Course collection and then
removing that course from the Student's courses collection using
student.Courses.remove(cours) and then calling SaveChanges. We can also add new courses
in the collection but we skip that part here. So this way just call to SaveChanges will save all
your activity of Many-to-Many relation entities in connected scenario.
Disconnected Scenario:
Following code saves modified Student and Course(for that student) to the database in
disconnected scenario:
Student student = null; using (var ctx = new SchoolDBContext()) { //Disable LazyLoading in disconnected scenario ctx.ContextOptions.LazyLoadingEnabled = false; student = (from s in ctx.Students.Include("Courses") where s.StudentName == "student3"
www.entityframeworktutorial.net
20
select s).FirstOrDefault<Student>(); } student.StudentName = "Updated student3"; Course cours = student.Courses.FirstOrDefault<Course>(); //removing first course from student's existing courses student.Courses.Remove(cours); using (var newCtx = new SchoolDBContext()) { newCtx.ContextOptions.LazyLoadingEnabled = false; //fetch existing student var dbStudent = (from s in newCtx.Students.Include("Courses") where s.StudentID == student.StudentID select s).FirstOrDefault<Student>(); var newCourses = student.Courses.ToList<Course>(); var dbCourses = dbStudent.Courses.ToList<Course>(); //You may skip this if you update only courses of the student but not student itself. newCtx.ApplyCurrentValues<Student>("Students", student); //new course or exiting courses added to student's courses var addedCourses = newCourses.Except(dbCourses, cs => cs.CourseId).ToList<Course>(); var deletedCourses = dbCourses.Except(newCourses, cs => cs.CourseId).ToList<Course>(); addedCourses.ForEach(cs => dbStudent.Courses.Add(cs)); deletedCourses.ForEach(cs => dbStudent.Courses.Remove(cs)); newCtx.SaveChanges(); }
www.entityframeworktutorial.net
21
As you can see in above code, we do the same thing as we did it to update One-to-Many entity
graph in disconnected scenario. First we re-fetch existing student from the database using new
context object. Then we find the added courses using “Except” method of collection by matching
CourseId of new Course collection and existing course collection.
E.g. var addedCourses = newCourses.Except(dbCourses, cs =>
cs.CourseId).ToList<Course>();
Above statement will return list of course whose CourseId doesn’t match with existing CourseId.
So here, all the new courses whose CourseId is zero and the existing course which is currently
not assigned to student will be returned. Here we have to consider two types of added courses.
Course which is new and the Course which is not new means already in the database but not
assigned to the student. So be careful of this.
So the same way we find the updated courses and deleted courses from the collection. Then we
add, update or delete the respected courses in new context and then call SaveChanges.
So thus we have to find added, updated and deleted Many-to-Many entities from the collection
and do CUD operation in disconnected scenario.
Page 32: Delete One-to-One Entities with ObjectContext:
Here, we will see how to delete an StudentAddress entity which has One-to-One relation with
Student entity in connected and disconnected scenario.
Connected Scenario: Following code deletes the student’s address from StudentAddress table in the database:
using (var ctx = new SchoolDBContext()) { Student student = (from s in ctx.Students where s.StudentName == "Student1" select s).FirstOrDefault<Student>(); StudentAddress sAddress = student.StudentAddress; ctx.StudentAddresses.DeleteObject(sAddress); ctx.SaveChanges(); }
As you can see in above code that we just delete the StudentAddress entity from
StudentAddress entityset and then calling SaveChanges.
www.entityframeworktutorial.net
22
Disconnected Scenario:
Following code deletes the student’s address from StudentAddress table in the database in
disconnected scenario:
Student student = null; using (var ctx = new SchoolDBContext()) { student = (from s in ctx.Students.Include("StudentAddress") where s.StudentName == "Updated POCOProxyInDisconnectedScenario student2" select s).FirstOrDefault<Student>(); } // Delete StudentAddress entity using different context using (var newCtx = new SchoolDBContext()) { newCtx.StudentAddresses.Attach(student.StudentAddress); newCtx.StudentAddresses.DeleteObject(student.StudentAddress); newCtx.SaveChanges(); }
As you can see in above code, we first attach StudentAddress in the StudentAddress entityset
of new context and then we use DeleteObject method of StudentAddress entityset which
deletes it from the entityset of new context. And finally call to SaveChanges will send the delete
query to the database which actually deletes the address from the StudentAddress table.
Page 33: Delete One-to-Many Entities with ObjectContext:
Connected Scenario:
Following code deletes the teacher for standard which has One-to-Many relationship from the
database in connected scenario:
using (var ctx = new SchoolDBContext()) { //fetching existing standard from the db Standard std = (from s in ctx.Standards where s.StandardName == "standard3" select s).FirstOrDefault<Standard>(); //getting first teacher to be removed
www.entityframeworktutorial.net
23
Teacher tchr = std.Teachers.FirstOrDefault<Teacher>(); //removing teachers if (tchr != null) ctx.Teachers.DeleteObject(tchr); //Do not use std.Teachers.Remove(tchr). It will give exception. //This statement doesn't remove teacher from teachers collection but it trying to //remove relationship. //std.Teachers.Remove(tchr); ctx.SaveChanges(); }
As you can see in above code, we remove the teacher by ctx.Teachers.DeleteObjct(teacher).
This will delete the teacher from the database table. Do not use std.Teacher.Remove(teacher)
because this statement will try to delete the standard and teacher relationship. So be careful
while deleting.
Disconnected Scenario:
Following code deletes the teacher for standard (which has One-to-Many relationship) from the
database in disconnected scenario:
Standard std = null; using (var ctx = new SchoolDBContext()) { ctx.ContextOptions.LazyLoadingEnabled = false; //fetching existing standard from the db std = (from s in ctx.Standards.Include("Teachers") where s.StandardName == "standard3" select s).FirstOrDefault<Standard>(); } //Creating new context using (var newCtx = new SchoolDBContext()) { //getting first teacher to be removed Teacher tchr = std.Teachers.FirstOrDefault<Teacher>(); newCtx.Teachers.Attach(tchr); //removing teachers newCtx.Teachers.DeleteObject(tchr);
www.entityframeworktutorial.net
24
//Do not use std.Teachers.Remove(tchr). It will give exception. //This statement doesn't remove teacher from teachers collection //but it trying to remove relationship. //std.Teachers.Remove(tchr); newCtx.SaveChanges(); }
As you can see in above code, we first attach the teacher entity in Teachers entityset in new
context object. Then we delete it from the collection. Thus you can delete the One-to-Many
entity graph in disconnected scenario.
Page 34: Delete Many-to-Many Entities with ObjectContext:
Connected Scenario:
Following code deletes the course from student's courses in connected scenario. This will
delete row in StudentCourse table but not delete the actual course from Course table in the
database:
using (var ctx = new SchoolDBContext()) { Student student = (from s in ctx.Students where s.StudentName == "Student3" select s).FirstOrDefault<Student>(); Course cours = student.Courses.FirstOrDefault<Course>(); //removing course from student student.Courses.Remove(cours); ctx.SaveChanges(); }
As you can see in above code, we do student.Courses.Remove(cours) because student and
courses has Many-to-Many relation. So thus you can delete Many-to-Many entity graph in
connected scenario.
Disconnected Scenario:
Following code deletes the course from student’s courses in disconnected scenario:
www.entityframeworktutorial.net
25
Student student = null; using (var ctx = new SchoolDBContext()) { ctx.ContextOptions.LazyLoadingEnabled = false; student = (from s in ctx.Students.Include("Courses") where s.StudentName == "student3" select s).FirstOrDefault<Student>(); } Course cours = student.Courses.FirstOrDefault<Course>(); //removing course from student student.Courses.Remove(cours); using (var newCtx = new SchoolDBContext()) { var dbStudent = (from s in newCtx.Students.Include("Courses") where s.StudentID == student.StudentID select s).FirstOrDefault<Student>(); var deletedCourses = dbStudent.Courses.Except(student.Courses, cs => cs.CourseId).ToList<Course>(); deletedCourses.ForEach(cs => dbStudent.Courses.Remove(cs)); newCtx.SaveChanges(); }
As you can see in above code, we re-fetch the student from the database and then finding
deleted courses whose CourseId doesn’t match with new course collection using
dbStudent.Course.Except() method. Then we remove it from courses collection of student.
Make sure that LazyLoading is disabled otherwise it will give exception.
Thus you can delete Manny-to-Many entity graph from the database.
www.entityframeworktutorial.net
26
Page 35: Add/Update Self-Tracking Entities with ObjectContext:
As you know that self-tracking entities are those entities who keep tracks its own changes in
different layers or tier where object context is not tracking the entities. We will use WCF service
to use STEs in different tier for our demo. So we will see client side code and WCF service code
to add, update and delete self-service entity graph.
We have created STEs using T4 templates and using those entities in WCF service project. So
let’s see WCF service code first.
WCF Service Code:
Following code shows UpdateStudent web method of WCF service:
public void UpdateStudent(Student student) { using (var ctx = new SchoolDBContext()) { ctx.Students.ApplyChanges(student); ctx.SaveChanges(); } }
As you can see in the above code, in the UpdateStudent webmethod, we just call
ApplyChanges method of Students entityset and passing updated student entity which comes
from client and then call SaveChanges method of context as usual.
So here we don’t have to tell anything to context or use AddObject method to add new entity
only ApplyChanges will serve our purpose because STE maintains the state of each entity in
entity graph. So this is the magic with self-tracking entities.
We will use same ApplyChanges method to update other entity graph to the database. So I
haven’t shown the code for update here. I leave it to you.
Client side Code:
Following code shows how client create the new Student, Standard, StudentAddress, Teacher
and Course entity and use WCF service to save it as a new row in respective database tables:
Student student = new Student(); student.StudentName = "Student 1"; Standard std = new Standard();
www.entityframeworktutorial.net
27
std.StandardName = "standard 1"; std.Description = "Added using wcf service"; student.Standard = std; StudentAddress sAddress = new StudentAddress(); sAddress.Address1 = "Address1"; sAddress.Address2 = "Address2"; sAddress.City = "city"; sAddress.State = "state"; student.StudentAddress = sAddress; Teacher tchr = new Teacher(); tchr.TeacherName = "teacher1"; tchr.Standard = std; Course cs1 = new Course(); cs1.CourseName = "Course 1"; cs1.Location = "City test"; cs1.Teacher = tchr; Course cs2 = new Course(); cs2.CourseName = "Course 2"; cs2.Location = "City test"; cs2.Teacher = tchr; student.Courses.Add(cs1); student.Courses.Add(cs2); //Create the WCF Service client to update the student in different tier or server using (SchoolServiceClient schoolService = new SchoolServiceClient()) { schoolService.UpdateStudent(student); }
As you can see in above code, we just call UpdateStudent webmethod which will insert rows in
respected tables for the entities. Simple, isn’t it?
www.entityframeworktutorial.net
28
Page 36: Delete Self-Tracking Entities with ObjectContext:
We will how to delete in self-service entity graph. We will see only client side code because
WCF service side code will remain same as Add/Update STE.
Client side Code:
Following code shows how to get the Student entity and then update by deleting one of the
Course from Student’s Courses collection and also delete the StudentAddress:
//using WCF service client using (SchoolServiceClient schoolService = new SchoolServiceClient()) { //use service to get the student Student student = schoolService.GetStudent("Student 1"); student.StudentName = "Updated Student 1"; // removing first course in Many-toMany relation if (student.Courses.Count > 1) student.Courses.RemoveAt(0); //Deleting student address in One-to-One relation if (student.StudentAddress != null) student.StudentAddress.MarkAsDeleted<StudentAddress>(); schoolService.UpdateStudent(student); }
As you can see that we use MarkAsDelete<> for StudentAddress to delete it. MarkAsDelete<>
is an extension method for self-tracking entity which mark entity as deleted. So it does
ApplyChanges at WCF service side, it automatically deletes the entity and deletes the row in
database table which is marked as deleted.
So this way you can perform deletion in self-tracking entities.
www.entityframeworktutorial.net
29
Page 37: Stored Procedure in Entity Framework:
Entity Framework has ability to automatically build native commands for database based on
your LINQ to Entities or Entity SQL queries, as well as build the commands for inserting,
updating, or deleting data, you may want to override these steps and use your own predefined
stored procedures. You can use stored procedures mainly for either to get the data or
add/update/delete the records to one or multiple database tables.
Stored procedures and user-defined functions (UDFs) in the database are represented as
functions in entity framework. So you won’t have any entity or other stuff for stored procedures
in the EDM designer. But you have to perform some extra steps to make stored procedures
work in the entity framework which we will see soon.
One important point to remember is that the EDM designer doesn’t allow you to write stored
procedures and bring them into the database. That means it doesn’t matter whether you opt for
the code-first, model-first or database-first approach. You always have to create your stored
procedures in the database and later import them into the EDM
In the next chapter, we will see how to work with Stored Procedures for CRUD operation using
Entity Framework 4.1.
Page 38: Data read using Stored Procedure:
Here, we will use stored procedure to get the Courses by Student. So we will create following
"GetCoursesByStudentId" stored procedure:
CREATE PROCEDURE [dbo].[GetCoursesByStudentId] -- Add the parameters for the stored procedure here @StudentId int = null AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. SET NOCOUNT ON; -- Insert statements for procedure here select c.courseid, c.coursename,c.Location from student s left outer join studentcourse sc on sc.studentid = s.studentid left outer join course c on c.courseid = sc.courseid where s.studentid = @StudentId END
www.entityframeworktutorial.net
30
Now, you have to perform two steps to use this stored procedure in entity framework.
1. Add the stored procedure in EDM 2. Add function import.
Add stored procedure in EDM: As we added tables in the EDM, the same way you can add
stored procedures in the EDM. If you want to add it in existing EDM the right click on designer
and click on "Update model from database..". This will popup update wizard where you can
select stored procedures and add it.
When you click on "Finish", you won't find any changes in the designer that's because stored
procedure is not being treated as entity. This step will only add stored procedure in storage
model. You can see it in XML view of the designer.
www.entityframeworktutorial.net
31
Wait a minute.. still you cannot use this stored procedure because Entity Framework doesn't
allow a stored procedure to be queried until it's mapped in the EDM. So now we have to map
this stored procedure to the conceptual model. To do that we have to perform second step,
"Add function import".
Add function import: Now in this step, we will import a function for the stored procedure. To do
that, right click on the designer surface and select "Model Browser". Here you can see your
stored procedure by expanding "Stored Procedures" node of SchoolDBModel.Store. Now, right
click on your stored procedure and select "Add function import..".
This will popup "Add Function Import":
www.entityframeworktutorial.net
32
Here, you can select four types of return values: None, Scalars, Complex and Entities. Let's see
each of these:
None: it means stored procedure will not return any value.
Scalars: it means stored procedure will return single value of selected type like binary, Boolean,
byte etc.
Complex: It means stored procedure will return complex type which is only on conceptual
model but not in database table. You can create complex type here only by first clicking on ‘Get
Column Information’ which will get the schema of stored procedure and then click on ‘Create
New Complex Type’ which will generate complex type.
Entities: it means stored procedure will return collection of selected entities.
www.entityframeworktutorial.net
33
In our case, stored procedure will return collection of Course entity. Click ‘OK’. This will update
your conceptual model and bring stored procedure in conceptual model under function import.
Now you can query this stored procedure in entity framework using context as following:
using (var ctx = new SchoolDBEntities()) { IList<Course> courseList = ctx.GetCoursesByStudentId(1).ToList<Course>(); //do something with courselist here }
Make sure that stored procedure returns the same columns as you have in course entity
otherwise it will give you exception. If you use POCO entities then regenerate context from T4
template to include function import in the context.
So this way you can do read operation with stored procedure in entity framework.
www.entityframeworktutorial.net
34
Page 39: Add/Update data using Stored Procedure:
If you have existing stored procedures to Insert, Update and Delete the record in the database
table then you can use those stored procedures for CUD operation instead of entity framework's
default saving mechanism using EntitySet. So let's see how we can use stored procedure to
insert, update and delete the student records in the database.
First, you have to write stored procedure for Insert, Update and Delete for student. Following is
a "sp_InsertStudentInfo" stored procedure which inserts the row in student table.
CREATE PROCEDURE [dbo].[sp_InsertStudentInfo] -- Add the parameters for the stored procedure here @StandardId int = null, @StudentName varchar AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. SET NOCOUNT ON; INSERT INTO [SchoolDB].[dbo].[Student] ([StudentName] ,[StandardId]) VALUES ( @StudentName, @StandardId ) SELECT SCOPE_IDENTITY() AS StudentId
Similar way you can write update and delete procedure.
Now you have to add this stored procedure to EDM. After adding stored procedure, click on
Student entity on designer and select "Stored Procedure Mapping". Here you can select our
created stored procedure "sp_InsertStudentInfo" as insert function. The same way you can
select other stored procedure for update and delete function as well:
www.entityframeworktutorial.net
35
Once you select the stored procedure, it will automatically assign the parameters for it as below.
So now context will use mapped stored procedure for adding, updating and deleting the student
entity.
Following code will use "sp_InsertStudentInfo" stored procedure for insert operation:
using (var ctx = new SchoolDBEntities()) { Student stud = new Student(); stud.StudentName = "New sp student"; stud.StandardId = 262; ctx.Students.AddObject(stud); ctx.SaveChanges(); }
www.entityframeworktutorial.net
36
Page 40: DefiningQuery in Entity Framework:
A defining query allows you to execute a native SQL statement that is specified in the
DefiningQuery element in the EntitySet element in the SSDL.
A defining query is commonly used to provide functionality similar to that provided by a
database view, but this native SQL statement will be in the .edmx file, not in the database. The
entityset in CSDL is used to surface data exposed by the defining query.
So here, we will see how we can execute same SQL using DifiningQuery which we used in
database view in previous chapter and get the same functionality as database view.
We will perform following three steps to create and use DefiningQuery:
1. Add DefiningQuery in SSDL 2. Add EntitySet in CSDL 3. Mapping between Conceptual and Storage EntitySets
Add DefiningQuery in SSDL: First of all, we have to add DefiningQuery in SSDL part of .edmx
file.
Here we will use following SQL query:
SELECT dbo.Student.StudentID, dbo.Student.StudentName, dbo.Course.CourseId, dbo.Course.CourseName FROM dbo.Student INNER JOIN dbo.StudentCourse ON dbo.Student.StudentID = dbo.StudentCourse.StudentId INNER JOIN dbo.Course ON dbo.StudentCourse.CourseId = dbo.Course.CourseId
So now open .edmx file in XML editor and EntitySet in SSDL (first part in XML view) as
following:
<EntitySet Name="StudentCourseView" EntityType="SchoolDBModel.Store.StudentCourseView" store:Type="Views" store:Schema="dbo" store:Name="StudentCourseView"> <DefiningQuery> SELECT dbo.Student.StudentID, dbo.Student.StudentName, dbo.Course.CourseId, dbo.Course.CourseName FROM dbo.Student INNER JOIN dbo.StudentCourse ON dbo.Student.StudentID = dbo.StudentCourse.StudentId INNER JOIN dbo.Course ON dbo.StudentCourse.CourseId = dbo.Course.CourseId
www.entityframeworktutorial.net
37
</DefiningQuery> </EntitySet>
Now we have to add EntityType in same SSDL as we mention in EntityType attribute of
EntitySet above.
<EntityType Name="StudentCourseView"> <Key> <PropertyRef Name="StudentId" /> <PropertyRef Name="CourseId" /> </Key> <Property Name="StudentId" Type="int" Nullable="false" StoreGeneratedPattern="Identity" /> <Property Name="CourseId" Type="int" Nullable="false" StoreGeneratedPattern="Identity" /> <Property Name="StudentName" Type="varchar" MaxLength="50" /> <Property Name="CourseName" Type="varchar" MaxLength="50" /> </EntityType>
So this way you have added DefiningQuery in SSDL. Now we will add EntitySet in CSDL which
will collect the data returned by DefiningQuery.
Add EntitySet in CSDL:
We can add EntitySet in CSDL from the designer itself. To add EntitySet in CSDL, right click on
designer surface and click Add ->Entity.. this will open Add Entity popup.
www.entityframeworktutorial.net
38
Enter Entity name as StudentCourseViewEntiy. Uncheck Create key property and click ‘OK’.
This will put StudentCourseViewEntity in designer.
Now add following scalar properties by right clicking on StudentCourseViewEntity in designer
and add scalar property:
StudentId - Int32 – NotNull
CourseId – Int32 – NotNull
StudentName – String – NotNull
CourseName – String – NotNull
Mark StudentId and CourseId property as EntityKey = true from property window. So this way
you can add EntitySet in CSDL from designer. Now we will map both the EntitySet.
Mapping between Conceptual and Storage EntitySets:
To map Conceptual and Storage EntitySets from the designer, right click on
‘StudentCourseViewEntity’ entity on designer and select ‘Table Mapping’. In the Table Mapping,
www.entityframeworktutorial.net
39
select ‘StudentCourseView’ which contains DefiningQuery in SSDL. This will automatically map
properties of both EntitySet based on name and type as below:
So this way you can use DefiningQuery to write any native-SQL query for the database.
Now you can use DefiningQuery using context as following:
using (var ctx = new SchoolDBEntities()) { IList<StudentCourseViewEntity> studentCourseList = (from sc in ctx.StudentCourseViewEntities where sc.StudentId == 226 select sc).ToList<StudentCourseViewEntity>(); }
Page 41: Data binding with ASP.Net application:
Now let’s see how we can bind Student entity graph to GridView in ASP.Net.
First of all , create the ASP.Net web application project. Now drag & drop GridView and
EntityDataSource from Data part in Default.aspx:
www.entityframeworktutorial.net
40
Now before you configure EntityDataSource, you have to add connection string in web.config. I
have following connection string in web.config:
<add name="SchoolDBEntities" connectionString="metadata=res://*/DBModel.SchoolDBModel.csdl|res://*/DBModel.SchoolDBModel.ssdl|res://*/DBModel.SchoolDBModel.msl;provider=System.Data.SqlClient;provider connection string="Data Source=.;Initial Catalog=SchoolDB;Integrated Security=True;MultipleActiveResultSets=True"" providerName="System.Data.EntityClient" />
Now go to design view of Default.aspx and click on configure EntityDataSource.
Select Named Connection from dropdown. This dropdown shows name of connection string in
your web.config. We have "SchoolDBEntities”"as connection string name so dropdown will have
it.
www.entityframeworktutorial.net
41
Select DefaultContainerName also and click "Next":
Here, select "Students" EntitySet because we are going to display Student information in the
GridView. Click "Finish".
Now we want to display Standard name instead of StandardId. So we have to get the Standard
entity which is navigation property in the Student EntitySet. So for that, select EntityDataSource
and press F4 to open property window. Here you set Include property value to "Standard":
www.entityframeworktutorial.net
42
Now to configure GridView, click on "Configure GridView and choose "EntityDataSource1" as
data source. This will automatically display columns for Students with StandardID. Now to
display StandardName instead of StandardId, remove the StandardId column and write
following code in TemplateField of GridView:
<asp:TemplateField HeaderText="Standard Name" SortExpression="StandardName"> <EditItemTemplate> <asp:Label ID="Label1" runat="server" Text='<%# Eval("Standard.StandardName") %>'> </asp:Label> </EditItemTemplate> <ItemTemplate> <asp:Label ID="Label2" runat="server" Text='<%# Bind("Standard.StandardName") %>'> </asp:Label> </ItemTemplate> </asp:TemplateField>
Now you are done. Run the project and you will get following display:
www.entityframeworktutorial.net
43