Back to: Entity Framework Tutorials For Begineers and Professionals
Saving Disconnected Entity in Entity Framework
In this article, I am going to discuss How to Save Disconnected Entities in the Entity Framework with Examples. Please read our previous article where we discussed the Different Methods to Attach Disconnected Entities to the Context Object in the Entity Framework. We are going to work with the same example that we created in our Introduction to Entity Framework Database First Approach article. Please read our introduction to Entity Framework Database First article before proceeding to this article.
What is Disconnected Entity in the Entity Framework?
The Entities which are not tracked by the context object (DbContext object) are known as disconnected entities in Entity Framework and at the end of this article, you will learn how to INSERT, UPDATE and DELETE a disconnected entity in Entity Framework. You will also understand how to INSERT, UPDATE and DELETE a disconnected Entity Graph in Entity Framework
In general, the Entity Framework DbContext instance automatically tracks the entities that are returned from the database, and any changes that we made to these entities are also tracked by the context object and are updated in the database when the SaveChanges method is called on the context object.
As discussed in our previous article, sometimes the entities are fetched or queried using one context object and then saved (or updated or deleted) using a different context object. In this case, the second context object needs to know whether the entities are new (should be inserted) or existing (should be updated or deleted) in the database. And we have already discussed in our previous article how we can attach a disconnected entity graph to the context object using DbContext.Entry Method, DbSet.Add Method and DbSet.Attach Method with Examples.
How to Save a Disconnected Entity in Entity Framework?
In Entity Framework, saving data in the disconnected environment is different than saving data in the connected environment. In a disconnected environment, the context object (an instance of DbContext class) is not aware of the state of the entities whether the entities are new or existing entities. Such entities are called Disconnected Entities. In this case i.e. in the disconnected scenario, first of all, you need to attach the disconnected entities to the context object with the appropriate Entity State in order to perform INSERT or UPDATE, or DELETE operations in the database.
In Entity Framework Disconnected Scenario, it is your key responsibility to figure out whether the entity is a new one or an existing entity, and based on this, you need to set the appropriate entity state. Here, the important question is how you will figure out whether the entity is a new one or an existing one. For this, you need to check the key property value i.e. the Primary key value of the entity.
If the key property value is greater than zero, then it is an existing Entity and you can set the Entity State as Modified. On the other hand, if the key value is zero, then it is a new entity and you need to set the Entity State as Added. For a better understanding, please have a look at the below diagram. First, we need to attach the Entity and while attaching the entity to the context object, we need to check the primary key column corresponding property value, and if the value is greater than 0, then we need to set the Entity State as Modified and if the value equals to zero then we need to set the Entity State as Added. Then when we call the SaveChanged method, the context object will generate either the INSERT or UPDATE SQL Statement based on the Entity State and will execute the SQL Statement in the database.
Example to Understand How to Save a new Entity in Entity Framework Disconnected Scenario
Please modify the Main method of the Program class as shown below. The following is an example of the Entity Framework Disconnected Scenario for adding a new entity.
using System; using System.Data.Entity; namespace DBFirstApproach { class Program { static void Main(string[] args) { //disconnected entity Student newDisconnectedStudent = new Student() { FirstName = "Steven", LastName = "Smith", StandardId = 1 }; using (var context = new EF_Demo_DBEntities()) { //To See the Generated SQL By Entity Framework context.Database.Log = Console.Write; //Setting the Entity State Based on the StudentID Property Value //If StudentId == 0, Means It is a New Entity, So, Set the Entity State as Added //If StudentId != 0 or > 0, Means It is an Existing Entity, So, Set the Entity State as Modified context.Entry(newDisconnectedStudent).State = newDisconnectedStudent.StudentId == 0 ? EntityState.Added : EntityState.Modified; Console.WriteLine($"Before SaveChanges Entity State: {context.Entry(newDisconnectedStudent).State}"); //Call SaveChanges to Update the Data in the Database context.SaveChanges(); Console.WriteLine($"After SaveChanges Entity State: {context.Entry(newDisconnectedStudent).State}"); } Console.Read(); } } }
In the above example, newDisconnectedStudent is a disconnected Entity, and the context object is not aware of its state. The context.Entry(newDisconnectedStudent).State = student.StudentId == 0? EntityState.Added : EntityState.Modified; statement sets the entity state as Added if the value of the key property StudentId is zero, otherwise it will set Entity State as Modified. In our example, while creating the newDisconnectedStudent object we have not set the StudentId value, so the default value is 0. Hence in this example, it will set the Entity State as Added and when you call the SaveChanges method it will execute the INSERT SQL Statement in the database. So, when you run the above example code, you will get the following output.
Example to Understand How to Update an Existing Entity in Entity Framework Disconnected Scenario
Please modify the Main method of the Program class as shown below. The following is an example of the Entity Framework Disconnected Scenario for updating an Existing Entity. In the below example, we set the value of StudentId property, and hence it will assign the entity State as the Modified state. And when you run the following application, it will generate an UPDATE SQL Statement.
using System; using System.Data.Entity; namespace DBFirstApproach { class Program { static void Main(string[] args) { //disconnected entity Student newDisconnectedStudent = new Student() { StudentId = 1, FirstName = "Virat", LastName = "Anushka", StandardId = 2 }; using (var context = new EF_Demo_DBEntities()) { //To See the Generated SQL By Entity Framework context.Database.Log = Console.Write; //Setting the Entity State Based on the StudentID Property Value //If StudentId == 0, Means It is a New Entity, So, Set the Entity State as Added //If StudentId != 0 or > 0, Means It is an Existing Entity, So, Set the Entity State as Modified context.Entry(newDisconnectedStudent).State = newDisconnectedStudent.StudentId == 0 ? EntityState.Added : EntityState.Modified; Console.WriteLine($"Before SaveChanges Entity State: {context.Entry(newDisconnectedStudent).State}"); //Call SaveChanges to Update the Data in the Database context.SaveChanges(); Console.WriteLine($"After SaveChanges Entity State: {context.Entry(newDisconnectedStudent).State}"); } Console.Read(); } } }
Output:
Example to Understand How to Delete a Disconnected Entity in Entity Framework:
Deleting a disconnected Entity in Entity Framework is very simple. You just need to set its state to Delete using the Entry() method as shown in the below example.
using System; using System.Data.Entity; namespace DBFirstApproach { class Program { static void Main(string[] args) { //Disconnected Entity Student DisconnectedStudent = new Student() { //The New Entity which we added in the First Example StudentId is 19 StudentId = 19 }; using (var context = new EF_Demo_DBEntities()) { //To See the Generated SQL By Entity Framework context.Database.Log = Console.Write; //Setting the Entity State as Deleted context.Entry(DisconnectedStudent).State = EntityState.Deleted; Console.WriteLine($"Before SaveChanges Entity State: {context.Entry(DisconnectedStudent).State}"); //Call SaveChanges to Update the Data in the Database context.SaveChanges(); Console.WriteLine($"After SaveChanges Entity State: {context.Entry(DisconnectedStudent).State}"); } Console.Read(); } } }
In the above example, the DisconnectedStudent instance contains only the StudentId key property. Deleting an entity using the entity framework only requires the key property. context.Entry(DisconnectedStudent).State = EntityState.Deleted attaches an entity to the context object and sets its state to Deleted. And when we call the SaveChanges method on the context object, it will generate and execute the DELETE SQL Statement in the database. So, when you run the above example, you will get the following output.
Disconnected Entity Graph in Entity Framework
As of now, we have discussed how to save a Disconnected Entity in Entity Framework. Now let us move a step further and understand how to save a disconnected entity graph (an entity with its related entities) in the entity framework. While working with large applications or real-time applications, it is a common requirement to save or update one entity with its related entities. So, let us see how to do the same using the Entity Framework Disconnected Environment.
Note: The point that you need to remember is unlike the connected Environment, updating an Entity Graph in a Disconnected Environment is a complex task. So, you need to do it carefully.
The problem in updating a disconnected entity graph is that the context object doesn’t know on the client side what operation was performed on it. As a result, the new context object doesn’t know the state of each entity. So, we need to identify the state of each entity in the entity graph before calling the SaveChages() method. There are different patterns that we can use to identify the state of each entity. In this article, we are going to use the key property to identify the entity state.
Using Key Property
You can use the key (Primary Key) property of each entity to determine the Entity State. The point that you need to remember is if the value of the key property is the default value of the CLR data type, then it is considered to be a new entity else it is considered to be an existing entity and accordingly, you need to set the Entity State. If a new Entity, you need to set the Entity State as Added and if it is an existing Entity, you need to set the Entity State as Modified.
For example, if the data type of a key property is int and the value of the key property is zero, then it is a new entity. If the value is non-zero, then it is an existing entity. Similarly, if the key property is the string data type and if the value of the key property is null, then it is a new entity and if the value is not null, then it is an existing entity. For the new entity, the Entity Framework will generate and execute the INSERT command while for the existing entity, the Entity Framework will generate and execute the UPDATE command in the database.
For a better understanding, please have a look at the following example. The following example demonstrates saving the Student entity graph with the related Standard, StudentAddress, and Course entities.
using System; using System.Collections.Generic; using System.Data.Entity; namespace DBFirstApproach { class Program { static void Main(string[] args) { //Creating the Disconnected Entity //Student Entity Graph (Student plus Standard, and Courses) //Student is the Main Entity //Standard, and Courses are the Child Entities var student = new Student() { //Root Entity with Empty key FirstName = "Steven", LastName = "Smith", Standard = new Standard() //Child Entity with key value { StandardId = 1, Description = "STD1 Description" }, Courses = new List<Course>() { new Course(){ CourseName = "AI" }, //Child Entity Empty key new Course(){ CourseId = 1 } //Child Entity with key value } }; student.StudentAddress = new StudentAddress() //Child Entity with StudentId as 0 { StudentId = student.StudentId, Address1 = "Address Line1", Address2 = "Address Line2" }; using (var context = new EF_Demo_DBEntities()) { //To See the Generated SQL context.Database.Log = Console.Write; //Set the Student entity state based on StudentId context.Entry(student).State = student.StudentId == 0 ? EntityState.Added : EntityState.Modified; //Set the Standard entity state based on StandardId context.Entry(student.Standard).State = student.Standard.StandardId == 0 ? EntityState.Added : EntityState.Modified; //Set the Course Entity state based on CourseId foreach (var course in student.Courses) { context.Entry(course).State = course.CourseId == 0 ? EntityState.Added : EntityState.Modified; } //Checking the Entity State of Each Entity of Etudent Entity Graph Console.WriteLine("Entity State Before Save Changes"); foreach (var entity in context.ChangeTracker.Entries()) { Console.WriteLine($"Entity Name: {entity.Entity.GetType().Name} Entity State: {entity.State}"); } //Finally Call the Save Changes Method to Update the Data in the Database context.SaveChanges(); Console.WriteLine("Entity State After Save Changes"); foreach (var entity in context.ChangeTracker.Entries()) { Console.WriteLine($"Entity Name: {entity.Entity.GetType().Name} Entity State: {entity.State}"); } } Console.Read(); } } }
In the above example, the student entity graph includes the related Standard, StudentAddress, and Course entities. The context sets an appropriate state for each entity based on the key property value. If it is zero, then it sets the Added state, otherwise, it sets the Modified state. The SaveChanges() method will execute the appropriate INSERT or UPDATE command to the database for each entity. So, when you run the above code, you will get the following output.
Entity State Before Save Changes Entity Name: Student Entity State: Added Entity Name: StudentAddress Entity State: Added Entity Name: Course Entity State: Added Entity Name: Standard Entity State: Modified Entity Name: Course Entity State: Modified Opened connection at 11-12-2022 17:04:17 +05:30 Started transaction at 11-12-2022 17:04:17 +05:30 UPDATE [dbo].[Course] SET [CourseName] = NULL, [TeacherId] = NULL WHERE ([CourseId] = @0) -- @0: '1' (Type = Int32) -- Executing at 11-12-2022 17:04:17 +05:30 -- Completed in 10 ms with result: 1 UPDATE [dbo].[Standard] SET [StandardName] = NULL, [Description] = @0 WHERE ([StandardId] = @1) -- @0: 'STD1 Description' (Type = AnsiString, Size = 100) -- @1: '1' (Type = Int32) -- Executing at 11-12-2022 17:04:17 +05:30 -- Completed in 2 ms with result: 1 INSERT [dbo].[Course]([CourseName], [TeacherId]) VALUES (@0, NULL) SELECT [CourseId] FROM [dbo].[Course] WHERE @@ROWCOUNT > 0 AND [CourseId] = scope_identity() -- @0: 'AI' (Type = AnsiString, Size = 100) -- Executing at 11-12-2022 17:04:17 +05:30 -- Completed in 6 ms with result: SqlDataReader INSERT [dbo].[Student]([FirstName], [LastName], [StandardId]) VALUES (@0, @1, @2) SELECT [StudentId] FROM [dbo].[Student] WHERE @@ROWCOUNT > 0 AND [StudentId] = scope_identity() -- @0: 'Steven' (Type = AnsiString, Size = 100) -- @1: 'Smith' (Type = AnsiString, Size = 100) -- @2: '1' (Type = Int32) -- Executing at 11-12-2022 17:04:17 +05:30 -- Completed in 2 ms with result: SqlDataReader INSERT [dbo].[StudentAddress]([StudentId], [Address1], [Address2], [Mobile], [Email]) VALUES (@0, @1, @2, NULL, NULL) -- @0: '22' (Type = Int32) -- @1: 'Address Line1' (Type = AnsiString, Size = 100) -- @2: 'Address Line2' (Type = AnsiString, Size = 100) -- Executing at 11-12-2022 17:04:17 +05:30 -- Completed in 1 ms with result: 1 INSERT [dbo].[StudentCourse]([StudentId], [CourseId]) VALUES (@0, @1) -- @0: '22' (Type = Int32) -- @1: '9' (Type = Int32) -- Executing at 11-12-2022 17:04:17 +05:30 -- Completed in 1 ms with result: 1 INSERT [dbo].[StudentCourse]([StudentId], [CourseId]) VALUES (@0, @1) -- @0: '22' (Type = Int32) -- @1: '1' (Type = Int32) -- Executing at 11-12-2022 17:04:17 +05:30 -- Completed in 0 ms with result: 1 Committed transaction at 11-12-2022 17:04:17 +05:30 Closed connection at 11-12-2022 17:04:17 +05:30 Entity State After Save Changes Entity Name: Student Entity State: Unchanged Entity Name: StudentAddress Entity State: Unchanged Entity Name: Course Entity State: Unchanged Entity Name: Standard Entity State: Unchanged Entity Name: Course Entity State: Unchanged
In the next article, I am going to discuss Asynchronous Programming using the Entity Framework. Here, in this article, I try to explain How to work with Disconnected Entities in the Entity Framework and I hope you enjoyed this Disconnected Entity in Entity Framework article. Please give your valuable feedback and suggestions about this article.