Professional Documents
Culture Documents
net
Projection:
Projection is a process of selecting data in different shape rather than specific entity being queried. There are many ways of projection. Lets 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<>
If you want to list of all students whose name is "Student1" (provided there are many students has same name) then use ToList<>:
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:
If you want to get only StudentName, StandardName and list of Courses for that student in single object then write following projection:
www.entityframeworktutorial.net
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.
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 doesnt 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.
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 MSDNs Entity Framework Query Samples.
//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
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.
www.entityframeworktutorial.net
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.
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
Disconnected scenario: Now lets 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 doesnt 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.
using (SchoolDBContext ctx = new SchoolDBContext()) { var stud = (from s in ctx.Students where s.StudentName == "Student1"
7
www.entityframeworktutorial.net
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
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 dont have to add Standard and StudentAddress entities into its respected EntitySet. Thus you can add One-to-One entity graph easily.
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.
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.
using (var ctx = new SchoolDBContext()) { Student student = (from s in ctx.Students where s.StudentName == "Student1" select s).FirstOrDefault<Student>(); 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 its StudentName and other StudentAddresss 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()) {
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 dont mark StudentAddress entity as modified then context will not update the StudentAddress table. So dont forget to mark entities as modified in disconnected 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";
14
www.entityframeworktutorial.net
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. Lets 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<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";
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<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
17
www.entityframeworktutorial.net
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 doesnt match with existing teachers TeacherId. So here, all the new teachers whose TeacherId is zero will be returned because it hasnt 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
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"
19
www.entityframeworktutorial.net
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(); }
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<Course>(); Above statement will return list of course whose CourseId doesnt 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.
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. 21
www.entityframeworktutorial.net
Disconnected Scenario: Following code deletes the students 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.
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
22
www.entityframeworktutorial.net
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);
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.
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 students 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<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 doesnt 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
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 dont 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 havent 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, isnt it?
27
www.entityframeworktutorial.net
//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.
28
www.entityframeworktutorial.net
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.
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..".
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<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.
33
www.entityframeworktutorial.net
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
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
36
www.entityframeworktutorial.net
</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.
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<StudentCourseViewEntity> studentCourseList = (from sc in ctx.StudentCourseViewEntities where sc.StudentId == 226 select sc).ToList<StudentCourseViewEntity>(); }
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:
<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.
40
www.entityframeworktutorial.net
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:
<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:
42
www.entityframeworktutorial.net
43