Entity Framework Tutorial Part 02
Short Description
Download Entity Framework Tutorial Part 02...
Description
www.entityframeworktutorial.net
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();
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(); 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 { 1
www.entityframeworktutorial.net
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();
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": 2
www.entityframeworktutorial.net
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(); 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 3
www.entityframeworktutorial.net
[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.
4
www.entityframeworktutorial.net
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..
5
www.entityframeworktutorial.net
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.
6
www.entityframeworktutorial.net
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 statemanager(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" 7
www.entityframeworktutorial.net
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.
8
www.entityframeworktutorial.net
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(); } 9
www.entityframeworktutorial.net
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(); } 10
www.entityframeworktutorial.net
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";
11
www.entityframeworktutorial.net
Teacher teacher1 = new Teacher(); teacher1.TeacherName = "teacher1"; teacher1.Standard = std; //assign teacher1 course1.Teacher = course2.Teacher = course3.Teacher =
for each courses teacher1; teacher1; 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.StudentName = "Updated Student1"; 12
www.entityframeworktutorial.net
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.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()) { 13
www.entityframeworktutorial.net
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(); std.StandardName = "Updated standard3"; 14
www.entityframeworktutorial.net
std.Description = "Updated standard"; //getting first teacher to be removed Teacher tchr = std.Teachers.FirstOrDefault(); //removing teachers (enable cascading delete for the teacher) if (tchr != null) ctx.Teachers.DeleteObject(tchr);
Teacher stdTeacher = std.Teachers.FirstOrDefault(); 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(); 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. 15
www.entityframeworktutorial.net
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-toEntities 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(); } 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(0); //get the second element to be removed Teacher deletedTchr = std.Teachers.ElementAt(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"; 16
www.entityframeworktutorial.net
// 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();
var newTeachers = std.Teachers.ToList(); var existingTeachers = exitingStandard.Teachers.ToList(); // 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
17
www.entityframeworktutorial.net
var updatedTeachers = exitingStandard.Teachers.Except(deletedTeachers, tchr => tchr.TeacherId); //Add new teachers to context addedTeachers.ToList().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().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. 18
www.entityframeworktutorial.net
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.StudentName = "Updated Student3"; Course cours = student.Courses.FirstOrDefault(); //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" 19
www.entityframeworktutorial.net
select s).FirstOrDefault(); } student.StudentName = "Updated student3";
Course cours = student.Courses.FirstOrDefault(); //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(); var newCourses = student.Courses.ToList(); var dbCourses = dbStudent.Courses.ToList(); //You may skip this if you update only courses of the student but not student itself. newCtx.ApplyCurrentValues("Students", student); //new course or exiting courses added to student's courses var addedCourses = newCourses.Except(dbCourses, cs => cs.CourseId).ToList(); var deletedCourses = dbCourses.Except(newCourses, cs => cs.CourseId).ToList(); addedCourses.ForEach(cs => dbStudent.Courses.Add(cs)); deletedCourses.ForEach(cs => dbStudent.Courses.Remove(cs)); newCtx.SaveChanges(); }
20
www.entityframeworktutorial.net
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(); 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(); 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. 21
www.entityframeworktutorial.net
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(); } // 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(); //getting first teacher to be removed 22
www.entityframeworktutorial.net
Teacher tchr = std.Teachers.FirstOrDefault(); //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(); } //Creating new context using (var newCtx = new SchoolDBContext()) { //getting first teacher to be removed Teacher tchr = std.Teachers.FirstOrDefault(); newCtx.Teachers.Attach(tchr); //removing teachers newCtx.Teachers.DeleteObject(tchr); 23
www.entityframeworktutorial.net
//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(); Course cours = student.Courses.FirstOrDefault(); //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:
24
www.entityframeworktutorial.net
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(); } Course cours = student.Courses.FirstOrDefault(); //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(); var deletedCourses = dbStudent.Courses.Except(student.Courses, cs => cs.CourseId).ToList(); 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.
25
www.entityframeworktutorial.net
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(); 26
www.entityframeworktutorial.net
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?
27
www.entityframeworktutorial.net
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(); 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.
28
www.entityframeworktutorial.net
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
29
www.entityframeworktutorial.net
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.
30
www.entityframeworktutorial.net
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":
31
www.entityframeworktutorial.net
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.
32
www.entityframeworktutorial.net
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 courseList = ctx.GetCoursesByStudentId(1).ToList(); //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.
33
www.entityframeworktutorial.net
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:
34
www.entityframeworktutorial.net
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(); } 35
www.entityframeworktutorial.net
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:
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 36
www.entityframeworktutorial.net
Now we have to add EntityType in same SSDL as we mention in EntityType attribute of EntitySet above.
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.
37
www.entityframeworktutorial.net
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, 38
www.entityframeworktutorial.net
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 studentCourseList = (from sc in ctx.StudentCourseViewEntities where sc.StudentId == 226 select sc).ToList(); }
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:
39
www.entityframeworktutorial.net
Now before you configure EntityDataSource, you have to add connection string in web.config. I have following connection string in web.config:
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.
40
www.entityframeworktutorial.net
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":
41
www.entityframeworktutorial.net
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:
Now you are done. Run the project and you will get following display:
42
www.entityframeworktutorial.net
43
View more...
Comments