find and solve || findandsolve.com
Please wait.....

Soft Deleting in ASP.NET Core - Entity Framework Core


In the entity framework soft Delete feature allows you to flag entities as Soft Delete instead of deleting them Hard Delete.Soft deletes aren't really deletes from the database,It's instead a IsDeleted flag marked on the specific row. Soft delete is just a term and it doesn't actually delete the row, but only updates a single column value, so no related entities are affected with this. However, we can do this with either entity framework or SQL trigger. 

Soft deleting involves something along the lines of adding an DateleteDate attribute which is usually contains value DateTime representing when the data was deleted to the database table that represents the  soft delete. 


In the given below example, I will be using a sample .NET 5 Core project to store EntityItems, EntityOrders and EntityOrderItems for a sample store. Initially,Controller will provide Item DELETE endpoint and it will actually delete the record from the database, but when update the solution EntityFrameworkCore.Triggered from NuGet package to setup triggers specific entity when it changes state to delete and instead mark the flag property and not actually delete from the database record.

Soft-delete solution example

Now, I am goint to create NET 5 Core solution which simply deletes the Item from the entity. There are three main entity models that we store in out database like as given below.

 public class EntityItem  
 {  
  public int ItemId{ get; set; }  
  public string Name { get; set; }  
  public int DisplayOrder { get; set; }  
  public virtual ICollection<EntityOrderItem> EntityOrderItem { get; set; }  
 }  
public class EntityOrder  {    public int OrderId{ get; set; }    public DateTime CreateDate{ get; set; }
   public ICollection<EntityOrderItem> EntityOrderItem { get; set; }
 }  
public class EntityOrderItem
 {
  public int OrderItemId {get;set;}
  public int OrderId { get; set; }
  public int ItemId { get; set; }
  public int Quantity { get; set; }
  public virtual EntityItem EntityItem { get; set; }
  public virtual EntityOrder EntityOrder { get; set; }
  }  

I am going to  creat an endpoint to delete Item directly from the Controller like  as give below.


public class EntityItemController : Controller  
{
    readonly DataContext _dataContext;
    public EntityItemController(DataContext  dataContext)
    {
        _dataContext= dataContext;
    }
    [HttpGet]
    public IActionResult Get()     {
      return _dataContext.EntityItem.Where().ToList();
    }
   [HttpDelete]
    public IActionResult  Delete(int id)
    {
        var item = _dataContext.EntityItem.Where(x=>x.ItemId==id).FirstOrDefault();
         if (item != null) {
            _dataContext.EntityItem.Remove(item);
        _dataContext.SaveChanges(); }
    }

Now first am going to run the tool create and include data seeding file.

seeding add "AddInitialEntityOrderItem" -o Seedings

And then I added the following SQL script and run this code like as given below.

DECLARE @computerId INT
INSERT INTO EntityItem([Name], [DisplayOrder]) Values ('Dell Core 7', 1)
SET @computerId = @@IDENTITY
DECLARE @ramId INT
INSERT INTO EntityItem([Name], [DisplayOrder]) Values ('8GB',2)
SET @ramId = @@IDENTITY
DECLARE @orderId INT
INSERT INTO EntityOrder(CreateDate) values (GETUTCDATE())
SET @orderId = @@IDENTITY
INSERT INTO EntityOrderItem(OrderId, ItemId, Quantity) Values (@orderId, @computerId ,3)
INSERT INTO EntityOrderItem(OrderId, ItemId, Quantity) Values (@ramId , @keyboardId, 1)
 

Handle Soft Delete Trigger

To introduce soft-delete functionality without actual change in the delete method, I will first add reference to EntityFrameworkCore.Triggered from nuget package.

dotnet add package EntityFrameworkCore.Triggered 

Write out triggers before is to mark entities that we would like to form soft-deleteable. I will simple do that with an interface which can also force the implementation of deleted flag property.

I decided to possess deleted flag stored as nullable DateTime, in order that once we have the worth present we all know that item is deleted and that we also know the time soft delete action occurred.

public interface ISoftDelete
{
  public DateTime? DeletedDate { get; set; }

Now I want to implement this ISoftDelete interface for each entity we want to apply soft-delete to. In  this will be EntityItem entity so therefore I need to reference and implement this interface.

 public class EntityItem : ISoftDelete
 {
  public int ItemId{ get; set; }
  public string Name { get; set; }
  public int DisplayOrder { get; set; }
  public virtual ICollection<EntityOrderItem> EntityOrderItem { get; set; }
 }  

There is another thing to try to to before we introduce the trigger which is creating and running the migration for the newly updated entity. Once the migration is executed, we'll have the new column in our Items table.

public class SoftDeleteTrigger : IBeforeSaveTrigger<ISoftDelete>  
{
    readonly DataContext _dataContext;
    public SoftDeleteTrigger(DataContext dataContext)
    {
        _dataContext = dataContext;
    }
    public async Task BeforeSave(ITriggerContext<ISoftDelete> context, CancellationToken cancellationToken)
    {
        if (context.ChangeType == ChangeType.Deleted)
        {
            var entry =_dataContext.Entry(context.Entity);
            context.Entity.DeletedDate= DateTime.UtcNow;
            entry.State = EntityState.Modified;
        }
        await Task.CompletedTask;
    }
}  

This trigger will now be hit whenever there's a change on any entity which implements ISoftDelete interface. This basically means if there's a requirement to introduce soft delete for the other entity, all we'd like to to is to implement this interface within the entity class and run the migration in so that we've the new field created as a column within the database.

I you check out the code of the trigger class, you'll see that it's quite simple. If change type is deleted, we just assign the present date time to DeletedOn property and that we switch the entity state to Modified in order that EF doesn't delete the entity once we perform saving of changes.

And the last step is to register triggers to Dependency Injection in Startup.cs class. We can register one by one trigger with extensions that come as part of EntityFrameworkCore.Triggered package.

services.AddDbContext<DataContext>(options =>  
{
    options.UseSqlServer(Configuration.GetConnectionString(DbContextConfigConstants.Configura_Connection_Name),
        x =>
        {
            x.MigrationsHistoryTable("__EFMigrationsHistory");
            x.MigrationsAssembly(this.GetType().Assembly.GetName().Name);
        }
    );
    options.UseTriggers(triggerOptions => {
    triggerOptions.AddAssemblyTriggers();  


Related information

Sundar  Neupane

Sundar Neupane

I like working on projects with a team that cares about creating beautiful and usable interfaces.

If findandsolve.com felt valuable to you, feel free to share it.

Comments



Report Response