EF Core 1.1 Soft (or logical) Delete

c# entity-framework entity-framework-core

Question

I'm trying to implement Soft Delete in a .NET Core 1.1.* Web App, backed by Entity Framework Core 1.1.*. I'm using Sql Server as my DB.

Migrating to .NET core 2.* is not an option at the moment.

After reading books, tuts and 3ds, I've implemented this feature using a Discriminator column. The deletion procedure is apparently working as expected. What's wrong is the data retrieval: deleted entities are still shown within my EF query results.

Current situation

Here's some C# code. I'll keep things as simple as possible
The interfaces:

// Soft deletion interface    
public intercace ISoftDeletable
{}

// Another interface for some shadow properties
public interface IEntity
{}

The base class:

public abstract class Entity : IEntity, ISoftDeletable
{
   public int MyBaseProp { get; set; }
}

One of my derived classes:

public class MyDerivedEntity: Entity
{
   public string Name { get; set; }

   public IList<MyChildEntity> Children { get; set; }

}

public class MyChildEntity: Entity 
{
    public string MyChildProp { get; set; }
}

The Context

public class MyContext: DbContext
{
   public MyContext(DbContextOptions<MyContext> options)
            : base(options)
   { }

   public DbSet<MyDerivedEntity> EntitiesToUse { get; set; }

   protected override void OnModelCreating(ModelBuilder builder)
   {
      foreach (var entity in builder.Model.GetEntityTypes())
      {                
         if (typeof(IEntity).IsAssignableFrom(entity.ClrType))
         {
           builder.Entity(entity.ClrType).Property<string>("MyShadowProperty");
         }

         if (typeof(ISoftDeletable).IsAssignableFrom(entity.ClrType))
         {
            // Discriminator column
            builder.Entity(entity.ClrType).HasDiscriminator("IsDeleted", typeof(bool)).HasValue(false);
            // Shadow Property       
            builder.Entity(entity.ClrType).Property(typeof(bool), "IsDeleted").IsRequired(true).HasDefaultValue(false);

            builder.Entity(entity.ClrType).Property(typeof(bool), "IsDeleted").Metadata.IsReadOnlyAfterSave = false;
         }
      }

      // Other model configurations

      base.OnModelCreating(builder);
   }

   // SaveChangesAsync are almost the same
   public override int SaveChanges()
   {
       AuditEntities();

       return base.SaveChanges();
   }

   private void AuditEntities()
   {

      foreach (EntityEntry<IEntity> entry in ChangeTracker.Entries<IEntity>())
      {
         // do something with MyShadowProperty...
      }
      foreach (EntityEntry<ISoftDeletable> entry in changeTracker.Entries<ISoftDeletable>().Where(w => w.State == EntityState.Deleted))
      {
        // Set the entity as Softly Deleted
        entry.Property("IsDeleted").CurrentValue = true;
        // Ensure the entity state is modified to prevend hard deletion
        entry.State = EntityState.Modified;
      }
   }
}

The Problem

Everything works as expected, except the data retrieval. Here's a sample call:

var results = await _context.EntitiesToUse.Include(e => e.SomeChildEntity).AsNoTracking();

I expect the results to include only available myDerivedEntities with .IsDeleted == false. The problem is that my deleted entities are not filtered out. Why?
Please, what's wrong with my code? Am I missing something?

Thank you all so much!

1
0
11/17/2017 2:41:39 PM

Expert Answer

Entity Framework Core 2.0 supports Global Query Filter

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<ISoftDeletable>().HasQueryFilter(e => !e.IsDeleted);

    base.OnModelCreating(modelBuilder);
}

You can find more info and examples here


I recommend you the built-in EF Core Global Query Filter but in some situations, Entity Framework Plus could also help.

Disclaimer: I'm the owner of this project

EF+ Query Filter allows you to filter DbSet Globally and by Instance.

// using Z.EntityFramework.Plus; // Don't forget to include this.
var ctx = new EntitiesContext();

ctx.Filter<IUser>(q => q.Where(x => !x.IsSystemUser ));

// SELECT * FROM Customers WHERE IsSystemUser = FALSE
var list = ctx.Customers.ToList();

Wiki: EF+ Query Filter

EDIT: Answer sub-question

Please, Is your library compatible with EF Core 1.1

Yes, it should be compatible with .NET Standard 1.3

2
8/22/2018 10:35:37 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow