简体   繁体   中英

Soft delete nested entities in EF Core

I have some nested entities, and I want to Auto soft-delete children of a record when I soft-delete that record(like hard-delete). How can I do this? And what is the best approach?

class Base
{
    bool isDeleted { set; get; }
}

class A : Base
{
    //Collection of B
}

class B : Base
{
    //Collection of C
}

class C : Base
{
    //Collection of D
}
....

For example:

Table A:

    Id    ForeignKey(B Id)    isDeleted 
    -------------------------------------
    1        1                 false

Table B:

   Id    ForeignKey(C Id)    isDeleted
   -------------------------------------
    1        1                 false
    1        2                 false
    1        3                 false

Table C:

    Id    ForeignKey(D Id)    isDeleted
    -------------------------------------
    1        1                 false
    1        2                 false
    2        3                 false
    2        4                 false
    3        5                 false
    3        6                 false

Code:

public void SoftDeleteA()
{
     //A.isDeleted = true;
     //???How to soft-delete related records in B,C,D ,...
     //SaveChanges();
}

Now when I soft-delete rows from A, all the rows of B and C must also be soft-deleted

There is no automatic way to achieve this with entity framework. Soft-delete is just a term and it doesn't actually delete the record, but only updates a single column value, so no related entities are affected with this. However, you can do this with either entity framework or SQL trigger. Since you want this to happen automatically, create an update trigger on table A and set isDeleted in related tables from updated record.

Articles related to trigger:
https://docs.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql?view=sql-server-ver15
https://www.tutorialgateway.org/after-update-triggers-in-sql-server/
https://www.sqlshack.com/triggers-in-sql-server/
https://www.aspsnippets.com/Articles/Simple-Insert-Update-and-Delete-Triggers-in-SQL-Server-with-example.aspx

With entity framework, you need fetch your parent along with all children first, modify them individually and save to database.

Example:

public A FetchA()
{
  return _context.A
          .Include(a=>a.CollectionB)
          .ThenInclude(b=>b.CollectionC)
          .FirstOrDefaultAsync();
}

public void SoftDeleteA()
{
    var a = FetchA();
    if(a !=null)
    {
       a.isDeleted = true;

       // loop through all related records and update them
       if(a.CollectionB?.Any()== true)
       { 
           foreach(var itemB in a.CollectionB)
           {
               itemB.isDeleted = true;

               // loop through all related records and update them
               if(itemB.CollectionC?.Any()== true)
               { 
                   foreach(var itemC in itemB.CollectionC)
                   {
                       itemC.isDeleted = true;
                   }         
               }
           }
       }

       // save changes at last
       _context.Update(a);
       await _context.SaveChangesAsync() ;
    }

}

OR if you have option to use SQL, just run the raw sql which is way faster than doing with EF.

Example:

update b
set isDeleted = a.isDeleted
from B b
inner join A a on b.ID = a.ID

I'm using something like Recursive Functions ;

class Base
{
      public virtual bool IsDeleted { get; set; }
      public virtual void DeleteMethod(int modifiedBy)
      {
          IsDeleted = true;
          ModifiedMethod(modifiedBy);
      }
}

class A : Base
{
    //Collection of B
    
    public override void DeleteMethod(int modifiedBy)
    {
        B.ForEach(oe => oe.DeleteMethod(modifiedBy));
        base.DeleteMethod(modifiedBy);
    }
}

class B : Base
{
    //Collection of C
    
    public override void DeleteMethod(int modifiedBy)
    {
        C.ForEach(oe => oe.DeleteMethod(modifiedBy));
        base.DeleteMethod(modifiedBy);
    }
}

class C : Base
{
    //Collection of D
    
    public override void DeleteMethod(int modifiedBy)
    {
        D.ForEach(oe => oe.DeleteMethod(modifiedBy));
        base.DeleteMethod(modifiedBy);
    }
}

when called some {ParentClass}.DeleteMethod() , its start operations to child classes. its same thing above answer but covering all inherited classes, not need write loop again every methods.

public async Task SoftDeleteAsync()
{
    ..
    A.DeleteMethod(userid);
    await context.SaveChangesAsync();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM