CRUD Operations in Entity Framework Core

CRUD Operations in Entity Framework Core (EF Core)

In this article, I will discuss CRUD Operations in Entity Framework Core (EF Core). Please read our previous article discussing Database Connection String in Entity Framework Core. We will work with the same example we have worked on so far.

CRUD Operations in Entity Framework Core (EF Core):

CRUD Operation means we need to perform Create, Retrieve, Update, and Delete Operations. To perform the Insert, Update, and Delete operations in Entity Framework Core, we have two persistence scenarios, i.e., connected and disconnected.

The same DbContext instance retrieves and saves the entities in the Connected scenario. On the other hand, the DbContext instance differs in the disconnected scenario (One DbContext instance retrieves the data, and another DbContext instance saves the data into the database). In this article, we will learn about performing database CRUD operations in the connected scenario. In our upcoming articles, we will discuss how to perform CRUD Operations in a disconnected scenario.

An entity that contains data in its scalar property will be either INSERTED, UPDATED, or DELETED, based on the State of the Entity. The Entity Framework Core API builds and executes the INSERT, UPDATE, and DELETE SQL Statements for the entities whose Entity State is Added, Modified, and Deleted, respectively, when the SaveChanges() method is called on the DbContext instance. The following diagram shows the connected scenario’s CUD (Create, Update, Delete) operations.

CRUD Operations in Entity Framework Core

Note: In the connected scenario, the same DbContext instance keeps track of all the entities, and when an entity is created, modified, or deleted, it automatically sets the appropriate State for the entity.

Enabling EF Core Logging:

We often need to see or log the generated SQL Script and Change Tracking information for debugging purposes. Entity Framework Core logging automatically integrates with the logging mechanisms of .NET Core.

We can use the simple Entity Framework Core (EF Core) logging mechanism to log the generated SQL while developing and debugging applications. This form of simple logging requires minimal configuration and no additional NuGet packages.

EF Core Logs can be accessed using the LogTo method, and we need to configure this method within the OnConfiguring method of the DbContext class. So, using the DbContextOptionsBuilder instance, we need to call the LogTo method and tell where to show the generated logs (Console.Write in our example) and what kind of logs (Information, Trace, Debug, Error, Warning, etc.) you want to display. So, please modify the EFCoreDbContext class as shown below to display the EF Core Log Information on the Console window.

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace EFCoreCodeFirstDemo.Entities
{
    public class EFCoreDbContext : DbContext
    {
        //Constructor calling the Base DbContext Class Constructor
        public EFCoreDbContext() : base()
        {
        }

        //OnConfiguring() method is used to select and configure the data source
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            //To Display the Generated the Database Script
            optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information);

            //Configuring the Connection String
            optionsBuilder.UseSqlServer(@"Server=LAPTOP-6P5NK25R\SQLSERVER2022DEV;Database=EFCoreDB1;Trusted_Connection=True;TrustServerCertificate=True;");
        }

        //OnModelCreating() method is used to configure the model using ModelBuilder Fluent API
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //use this to configure the model
        }

        //Adding Domain Classes as DbSet Properties
        public DbSet<Student> Students { get; set; }
        public DbSet<Standard> Standards { get; set; }
    }
}

Inside the OnConfiguring method, we have added the following statement. The following statement will log the generated SQL Script on the Console window.

optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information);

Create Operation using Entity Framework Core:

Create Operation means we need to add a new object. Adding a new object using Entity Framework Core is very simple. First, you need to create an instance of the Entity you want to add to the database. Once you created the instance of the entity, then you need to call the Add Method of the DbSet or DbContext class and pass the entity. Finally, call the SaveChanges method on the DbContext object, which will insert the new record into the database by generating the INSERT SQL Statement.

Note: The DbSet.Add and DbContext.Add methods add a new entity to a context (instance of DbContext), which will insert a new record in the database when you call the SaveChanges() method.

For a better understanding, modify the Main method of the Program class as follows. In the below example, context.Students.Add(newStudent) adds the newly created student entity to the context object with Added Entity State. EF Core introduced the new DbContext.Add method, which does the same thing as the DbSet.Add method. Most often, we are going to use the DbContext.Add by omitting the type parameter because the compiler will infer the type from the argument passed into the method. I have shown you three ways to add the student entity to the context object here.

using EFCoreCodeFirstDemo.Entities;
namespace EFCoreCodeFirstDemo
{
    public class Program
    {
        static void Main(string[] args)
        {
            try
            {
                //Create a new student which you want to add to the database
                var newStudent = new Student()
                {
                    FirstName = "Pranaya",
                    LastName = "Rout",
                    DateOfBirth = new DateTime(1988, 02, 29),
                    Height = 5.10m,
                    Weight = 72
                };

                //Create DBContext object
                using var context = new EFCoreDbContext();

                //Add Student Entity into Context Object

                //Method1: Add Student Entity Using DbSet.Add Method
                context.Students.Add(newStudent);

                //Method2: Add Student Entity Using DbContext.Add Method with Type Parameter
                //context.Add<Student>(newStudent);

                //Method2: Add Student Entity Using DbContext.Add Method without Type Parameter
                //context.Add(newStudent);

                //Now the Entity State will be in Added State
                Console.WriteLine($"Before SaveChanges Entity State: {context.Entry(newStudent).State}");

                //Call SaveChanges method to save student entity into database
                context.SaveChanges();

                //Now the Entity State will change from Added State to Unchanged State
                Console.WriteLine($"After SaveChanges Entity State: {context.Entry(newStudent).State}");

                Console.WriteLine("Student Saved Successfully...");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}"); ;
            }

            Console.ReadKey();
        }
    }
}

With the above changes, run the application and get the following output.

Create Operation using Entity Framework Core

If you verify the SQL Server database, you will see that the above entity is inserted into the Students database table, as shown in the image below.

Create Operation using Entity Framework Core

Update Operation in Entity Framework Core:

As discussed in the connected environment, the Entity Framework keeps track of all the entities retrieved using the context object. Therefore, when we modify any entity data, the Entity Framework will automatically mark the Entity State as Modified. When the SaveChanges method is called, it updates the updated data into the underlying database by generating the UPDATE SQL Statement.

The following code changes the student’s first and last names, whose ID is 1. The following example code is self-explained, so please go through the comment lines.

using EFCoreCodeFirstDemo.Entities;
namespace EFCoreCodeFirstDemo
{
    public class Program
    {
        static void Main(string[] args)
        {
            try
            {
                //Create DBContext object
                using var context = new EFCoreDbContext();

                //Fetch the Student from database whose Id = 1
                var student = context.Students.Find(1);

                if(student != null)
                {
                    //At this point Entity State will be Unchanged
                    Console.WriteLine($"Before Updating Entity State: {context.Entry(student).State}");

                    //Update the first name and last name
                    student.FirstName = "Prateek";
                    student.LastName = "Sahu";

                    //At this point Entity State will be Modified
                    Console.WriteLine($"After Updating Entity State: {context.Entry(student).State}");
                    
                    //Call SaveChanges method to update student data into database
                    context.SaveChanges();

                    //Now the Entity State will change from Modified State to Unchanged State
                    Console.WriteLine($"After SaveChanges Entity State: {context.Entry(student).State}");

                    Console.WriteLine("Student Updated Successfully...");
                }
                else
                {
                    Console.WriteLine("Invalid Student ID : 1");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}"); ;
            }

            Console.ReadKey();
        }
    }
}

In the above example, first, we retrieve the student from the database using the DbSet Find method, which generates a SELECT SQL query. When we modify the FirstName and LastName, the context object sets its Entity State to Modified. When we call the SaveChanges() method, it builds and executes the Update SQL statement in the database. Run the above code, and you will get the following output.

Update Operation in Entity Framework Core

Note: The context object in Entity Framework Core keeps track of the columns of an entity that are modified, and based on the modified columns, it will generate the UPDATE SQL statement. Here, it will use the primary key column in the where condition. If one column value is not updated, it will not include that column while updating the database. So you can see the UPDATE statement. It only includes the First Name and Last Name columns.

Delete Operation in Entity Framework Core:

We need to use the Remove method of the DbSet or DbContext object to delete an entity using Entity Framework Core. The DbSet or DbContext Remove method works for existing and newly added entities tracked by the context object.

Calling the Remove method on an existing entity being tracked by the context object will not immediately delete the entity from the database. Rather, it will mark the entity state as Deleted. When we call the SaveChanged method, the Entity will be deleted from the database by generating and executing the DELETE SQL Statement. Once the SaveChanges method is executed successfully, it will mark the entity state as Detached, which means the context object will not track the entity.

In the following example, we remove a student from the database whose StudentId is 1. The following example code is self-explained, so please go through the comment lines.

using EFCoreCodeFirstDemo.Entities;
namespace EFCoreCodeFirstDemo
{
    public class Program
    {
        static void Main(string[] args)
        {
            try
            {
                //Create DBContext object
                using var context = new EFCoreDbContext();

                //Find the Student to be deleted by Id
                var student = context.Students.Find(1);

                if (student != null)
                {
                    //At this point the Entity State will be Unchanged
                    Console.WriteLine($"Entity State Before Removing: {context.Entry(student).State}");
                    
                    //The following statement mark the Entity State as Deleted
                    context.Students.Remove(student);
                    //context.Remove<Student>(student);
                    //context.Remove(student);

                    //At this point, the Entity State will be in Deleted state
                    Console.WriteLine($"Entity State After Removing: {context.Entry(student).State}");

                    //SaveChanges method will delete the Entity from the database
                    context.SaveChanges();
                    
                    //Once the SaveChanges Method executed successfully, 
                    //the Entity State will be in Detached state
                    Console.WriteLine($"Entity State After Removing: {context.Entry(student).State}");
                }
                else
                {
                    Console.WriteLine("Invalid Student ID: 1");
                }
                
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}"); ;
            }

            Console.ReadKey();
        }
    }
}

In the above example, context.Students.Remove(student) marks the student entity state as Deleted. When we call the SaveChanges method on the Context object, the Entity Framework Core API generates and executes the DELETE SQL Statement in the database. Then, it will mark the entity state as Detached, which means the context object is no longer tracking this entity. Run the above application code, and you should get the following output.

CRUD Operations in EF Core

In the next article, I will discuss Entity States in Entity Framework Core. In this article, I try to explain CRUD Operations in Entity Framework Core (EF Core). I hope you enjoy this CRUD Operations in EF Core article.

Leave a Reply

Your email address will not be published. Required fields are marked *