Delete Records in Entity Framework Core

Delete Records in Entity Framework Core

Entity Framework Core API executes the DELETE statement in the database for the entities whose EntityState is set as Deleted. The Remove() method of the DbContext is used for deleting records from the database.

Delete Single Record

Consider the following code where Entity Framework Core Deletes Record by id. We are deleting Department with Id ‘3’.

var dept = new Department()
{
    Id = 3
};

context.Remove(dept);
await context.SaveChangesAsync();

Once the Remove() method is called for the department entity, the EF marks the 3rd id’s EntityState as Deleted. So when the SaveChangesAsync() method is called the 3rd department record is deleted from the database.

Entity Framework Core Delete Records by Id

Interesting EF Core tutorial: Configurations in Entity Framework Core

Delete Multiple Records

Use RemoveRange() method of DbContext to delete multiple entities on one go. The below codes remove 3 Department records with Id’s as – 1, 2 & 3 on one go.

List<Department> dept = new List<Department>()
{
    new Department() { Id=1 },
    new Department() { Id=2 },
    new Department() { Id=3 }
};

context.RemoveRange(dept);
await context.SaveChangesAsync();
If the key value specified in the Remove() or RemoveRange() method does not exist then EF Core will thrown exception of type DbUpdateConcurrencyException.

Cascade Delete

If an entity has relationship with other entities such as one-to-one or one-to-many then child entities may be affected when the parent entity is deleted. That is the child entities will be either deleted or their foreign key value is set to null. This is know as Cascade Delete behaviour in Entity Framework Core.

We have 2 entities Department and Employee having one-to-many relationship.

public class Department
{
    public int Id { get; set; }
    public string Name { get; set; }
 
    public ICollection<Employee> Employee { get; set; }
}
 
public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Designation { get; set; }
     
    public int DepartmentId { get; set; }
    public Department Department { get; set; } = null!;
}

By convention, this above relationship is configured as a Required, since the Employee.DepartmentId foreign key property is non-nullable. Entity Framework Core configures the Required relationships as Cascade Delete by default. This means the Related entities (which in this case is Employee) is also deleted when parent entity (which here is Department) is deleted.

The below code deletes a department, the EF Core will also delete all its related Employees i.e. Cascade Delete is performed.

var department = context.Department.OrderBy(e => e.Name).Include(e => e.Employee).FirstAsync();

context.Remove(department);

context.SaveChanges();
EF Core always applies cascading behaviors to tracked entities.

Severing a relationship – deleting orphans

Instead of deleting the child (here Employee), we could instead sever it’s relationship with the parent (here Deparment). This can be done by setting the reference navigation Employee.Department to null.

var department = context.Department.OrderBy(e => e.Name).Include(e => e.Employee).FirstAsync();

foreach (var e in department.Employee)
{
    e.Deparment = null;
}

context.SaveChanges();

Other way is to remove each Employee from the “Department.Employee” collection navigation:

var department = context.Department.OrderBy(e => e.Name).Include(e => e.Employee).FirstAsync();

department.Employee.Clear(); 

context.SaveChanges();

Cascading nulls

When their is a nullable foreign key property in the child entity then the foreign key value is set to null when the parent entity is deleted.

See the below code where this time DepartmentId property of Employee is made nullable – public int? DepartmentId { get; set; }.

public class Department
{
    public int Id { get; set; }
    public string Name { get; set; }
 
    public ICollection<Employee> Employee { get; set; }
}
 
public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Designation { get; set; }
     
    public int? DepartmentId { get; set; }
    public Department Department { get; set; }
} 

This means that the foreign key value will be set to null when the current principal/parent is deleted or is severed from the dependent/child.

Cascade delete in the database

Their are many databases that have Cascade Delete feature. If we have configured the cascade behavior in our model then EF Core will configure this cascade delete behavior in the database when the database is created using EnsureCreated or EF Core migrations.

Below is the SQL code for the Employee table that contains the ON DELETE CASCADE statement, see the last line.

CREATE TABLE [dbo].[Employee] (
    [Id]           INT           IDENTITY (1, 1) NOT NULL,
    [Name]         VARCHAR (100) NOT NULL,
    [Designation]  VARCHAR (25)  NOT NULL,
    [DepartmentId] INT           NOT NULL
);

GO
CREATE NONCLUSTERED INDEX [IX_Employee_DepartmentId]
    ON [dbo].[Employee]([DepartmentId] ASC);
GO
ALTER TABLE [dbo].[Employee]
    ADD CONSTRAINT [PK_Employee] PRIMARY KEY CLUSTERED ([Id] ASC);
GO
ALTER TABLE [dbo].[Employee]
    ADD CONSTRAINT [FK_Employee_Department] FOREIGN KEY ([DepartmentId]) REFERENCES [dbo].[Department] ([Id]) 
ON DELETE CASCADE;

In this case we can delete a Department without loading related Employee, the database will take care of deleting all the related empoyees.

For example in the below code the there is noInclude for Employee, so they are not loaded. SaveChanges in this case will delete just the Depatment, since that’s the only entity being tracked. The database will perform cascade delete automatically.

var department = context.Department.OrderBy(e => e.Name).FirstAsync();
context.Remove(department);
context.SaveChanges();
It should also be noted that the above code can result for the database giving an exception, if it is not configured for cascade delete.

Configuring Cascade Delete with Fluent API

With the help of Fluent API of Entity Framework Core we can define the Cascade Delete behaviour by 4 main ways. These are:

  • Cascade : Related entities are also deleted when parent entity is deleted.
  • ClientSetNull : Here foreign key is set to null.
  • ClientCascade: Make this configuration only apply to tracked entities, not the database. That is EF Core will perform cascade delete for tracked entities. Use this option when you get Microsoft.Data.SqlClient.SqlException (0x80131904) for cycles or multiple cascade paths.
  • Restrict : Prevents Cascade delete.
  • SetNull : The values of foreign key properties will be set to null.

Open the Database Context File of the application, there set the OnDelete() method to Cascade, ClientSetNull, Restrict, SetNull by using “DeleteBehavior” enum. Below we have maked it to Cascade.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Employee>(entity =>
    {
        entity.HasOne(d => d.Department)
            .WithMany(p => p.Employee)
            .HasForeignKey(d => d.DepartmentId)
            .OnDelete(DeleteBehavior.Cascade)
            .HasConstraintName("FK_Employee_Department");
    });
}

Note that we have to re-run the migrations for this behaviour to apply.

Now when we delete a record in the Department table then all the related records in the Employee table are also deleted automatically. This is because Deparment is the parent entity and Employee is a child entity.

Check the below code where we are deleting the 5th department id. So all employees that are in the 5th department are also deleted automatically.

var dept = context.Department.Where(e => e.Id == 5).Include(e => e.Employee).FirstOrDefault();
context.Remove(dept);
await context.SaveChangesAsync();

ClientCascade

The ClientCascade configuration will apply delete for tracked entities only, not the database.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Employee>(entity =>
    {
        entity.HasOne(d => d.Department)
            .WithMany(p => p.Employee)
            .HasForeignKey(d => d.DepartmentId)
            .OnDelete(DeleteBehavior.ClientCascade)
            .HasConstraintName("FK_Employee_Department");
    });
}

In the below code we load both a Department and the Empoyee they own, we then delete the Department.

Both the entities are tracked and so cascade delete of the Child i.e. Employee is also done when it’s parent Department is deleted.

var dept = await context.Department.SingleAsync(e => e.Name == "Designing");
var emp = await context.Employee.SingleAsync(e => e.Department == dept);

context.Remove(dept);

context.SaveChanges();

It is also important to note that in the below code we are not loading the related employee and so we will get an exception.

var dept = await context.Department.SingleAsync(e => e.Name == "Designing");
context.Remove(dept);
context.SaveChanges();

The exception says:

Microsoft.Data.SqlClient.SqlException: The DELETE statement conflicted with the REFERENCE constraint "FK_Employee_Department". The conflict occurred in database "Company", table "dbo.Employee", column 'DepartmentId'. The statement has been terminated.

Entity Framework Core CRUD Operations – DELETE RECORDS

It’s time we complete the CRUD OPERATIONS with the DELETE RECORDS feature. Recall, on the last tutorial Update Records in Entity Framework Core, we added the update records feature to it.

The delete records feature is created by adding a new Delete column on the table given on the Index view where all the entities are shown. The new delete column will contain a small form with a “delete” button. When the user will click this button, the form will submit and calls the Delete action method. The Entity Framework Core will delete the record on this Delete action method.

So start by adding the delete column on the Index.cshtml file for the Department controller, location is Views/Department.

@{
	ViewData["Title"] = "All Departments";
}
@model IEnumerable<Department>

<h1 class="bg-info text-white">All Departments</h1>
<a asp-action="Create" class="btn btn-secondary">Create</a>

<table class="table table-sm table-bordered">
	<tr>
		<th>ID</th>
		<th>Name</th>
		<th>Update</th>
		<th>Delete</th>
	</tr>
	@foreach (Department dept in Model)
	{
		<tr>
			<td>@dept.Id</td>
			<td>@dept.Name</td>
			<td>
                <a class="btn btn-sm btn-primary" asp-action="Update" asp-route-id="@dept.Id">
                    Update
                </a>
            </td>
			<td>
                <form asp-action="Delete" asp-route-id="@dept.Id" method="post">
                    <button type="submit" class="btn btn-sm btn-danger">
                        Delete
                    </button>
                </form>
            </td>
		</tr>
	}
</table>

The delete column will look as shown below.

entity framework core delete records

Next add the Delete action method to the DepartmentController.cs file as shown below.

using EFCoreExample.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace EFCoreExample.Controllers
{
    public class DepartmentController : Controller
    {
        private CompanyContext context;
        public DepartmentController(CompanyContext cc)
        {
            context = cc;
        }

        [HttpPost]
        public async Task<IActionResult> Delete(int id)
        {
            var dept = new Department() { Id = id };
            context.Remove(dept);
            await context.SaveChangesAsync();

            return RedirectToAction("Index");
        }
 
        //...

    }
}

Check this feature on the url – https://localhost:7018/Department.

We do the same thing for the Employee entity. So first add the Delete column to the Index.cshtml view of Employee Controller whose location is Views/Employee.

@{
	ViewData["Title"] = "All Employees";
}
@model IEnumerable<Employee>

<h1 class="bg-info text-white">All Employees</h1>
<a asp-action="Create" class="btn btn-secondary">Create</a>

<table class="table table-sm table-bordered">
	<tr>
		<th>ID</th>
		<th>Name</th>
		<th>Designation</th>
		<th>Department</th>
		<th>Update</th>
		<th>Delete</th>
	</tr>
	@foreach (Employee emp in Model)
	{
		<tr>
			<td>@emp.Id</td>
			<td>@emp.Name</td>
			<td>@emp.Designation</td>
			<td>@emp.Department.Name</td>
			<td>
                <a class="btn btn-sm btn-primary" asp-action="Update" asp-route-id="@emp.Id">
                    Update
                </a>
            </td>
			<td>
                <form asp-action="Delete" asp-route-id="@emp.Id" method="post">
                    <button type="submit" class="btn btn-sm btn-danger">
                        Delete
                    </button>
                </form>
            </td>
		</tr>
	}
</table>

Next add the Delete action on the EmployeeController.cs file.

[HttpPost]
public async Task<IActionResult> Delete(int id)
{
    var emp = new Employee() { Id = id };
    context.Remove(emp);
    await context.SaveChangesAsync();

    return RedirectToAction("Index");
}

Run the app and go to – https://localhost:7018/Employee. Here we can see the delete button against every employee record. Click on any one to delete the employee.

delete records entity framework core

You can get this CRUD feature on the app which is available for download.

DOWNLOAD

Conclusion

We learned how to delete records from EF Core. Both deletion of single and mulitple records covered. We also learned Cascade Delete topic and also completed the CRUD OPERATIONS app with the delete records feature.

SHARE THIS ARTICLE

  • linkedin
  • reddit
yogihosting

ABOUT THE AUTHOR

I hope you enjoyed reading this tutorial. If it helped you then consider buying a cup of coffee for me. This will help me in writing more such good tutorials for the readers. Thank you. Buy Me A Coffee donate