Open Closed

ISoftDelete not applied post delete in same execution even after flushing cache #9286


User avatar
0
AutosledDBP created

I'm encountering an issue related to ISoftDelete not working as expected in a parent-child relationship within the same execution context. I have an ABP 8.2.1 solution with multiple microservices. In this issue it is contained in one microservice application which is my core service.

Setup I have a CoreServiceDbContext configured as follows:

[ConnectionStringName(CoreServiceDbProperties.ConnectionStringName)]
public class CoreServiceDbContext : MyDbContext<CoreServiceDbContext>
{
    public CoreServiceDbContext(DbContextOptions<CoreServiceDbContext> options)
        : base(options)
    {
    }

    public DbSet<Shipment> Shipments { get; set; }
    public DbSet<Vehicle> Vehicles { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        builder.ConfigureCoreService();
    }
}

Extension method used in model creation:

public static class CoreServiceDbContextModelCreatingExtensions
{
    public static void ConfigureCoreService(this ModelBuilder builder)
    {
        Check.NotNull(builder, nameof(builder));

        if (builder.IsHostDatabase())
        {
            builder.Entity<Shipment>(b =>
            {
                b.ToTable("Shipments");
                b.HasMany(x => x.Vehicles)
                 .WithOne(x => x.Shipment)
                 .OnDelete(DeleteBehavior.Cascade);
                b.ConfigureByConvention();
            });

            builder.Entity<Vehicle>(b =>
            {
                b.ToTable("Vehicles");
                b.ConfigureByConvention();
            });
        }
    }
}

The parent entity Shipment looks like this:

public class Shipment : FullAuditedAggregateRoot<Guid>
{
    public virtual ICollection<Vehicle> Vehicles { get; set; }

    public virtual int VehicleCount { get; set; }
}

and Vehicle:

public class Vehicle : FullAuditedAggregateRoot<Guid>
{
   // More properties here
}

Repository Structure

The Vehicle entity is managed via a custom repository chain:

public interface IVehicleRepository : ICustomRepository<Vehicle, Guid> { }

public interface ICustomRepository<TEntity, TKey> : IRepository<TEntity, TKey>
    where TEntity : class, IEntity<TKey>
{
    Task<TEntity> GetAsync(TKey id, bool includeDetails, params Expression<Func<TEntity, object>>[] propertySelectors);

    Task<IEnumerable<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails, params Expression<Func<TEntity, object>>[] propertySelectors);
}

EF Core Implementation:

public abstract class EfCoreServiceRepository<TDbContext, TEntity, TKey> : EfCoreRepository<TDbContext, TEntity, TKey>, ICustomRepository<TEntity, TKey>
    where TDbContext : IEfCoreDbContext
    where TEntity : class, IEntity<TKey>
{
    public EfCoreServiceRepository(IDbContextProvider<TDbContext> dbContextProvider)
        : base(dbContextProvider)
    {
    }
}

public class EfCoreVehicleRepository : EfCoreServiceRepository<CoreServiceDbContext, Vehicle, Guid>, IVehicleRepository
{
    public EfCoreVehicleRepository(IDbContextProvider<CoreServiceDbContext> dbContextProvider)
        : base(dbContextProvider)
    {
    }
}

The repository structure for Shipment is the same.

Problem

When I delete a Vehicle like this:

await this.VehicleRepository.DeleteAsync(vehicleId);

And then I fetch it's parent entity, the parent still includes the soft-deleted vehicle if the GetAsync from shipment repository is executed right after the delete, in the same async execution.

var shipment = await _shipmentRepository.GetAsync(shipmentId);
var count = shipment.Vehicles.Count; // Still includes recently soft-deleted vehicle, therefore this number is always 1 more than what it should be.

What I tried:

  • I flushed the Redis cache entirely with redis-cli flushall, to rule out distributed caching — no effect.
  • I enabled autoSave in the DeleteAsync call — no change.

It appears that EF Core combined with ABP does not re-evaluate global query filter ISoftDelete for navigation properties.


9 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you call the await UnitOfWorkManager.Current.SaveChangesAsync(); after DeleteAsync?

    eg:

    await this.VehicleRepository.DeleteAsync(vehicleId);
    
    await UnitOfWorkManager.Current.SaveChangesAsync();
    
    var shipment = await _shipmentRepository.GetAsync(shipmentId);
    var count = shipment.Vehicles.Count;
    
  • User Avatar
    0
    AutosledDBP created

    hi

    Can you call the await UnitOfWorkManager.Current.SaveChangesAsync(); after DeleteAsync?

    eg:

    await this.VehicleRepository.DeleteAsync(vehicleId); 
     
    await UnitOfWorkManager.Current.SaveChangesAsync(); 
     
    var shipment = await _shipmentRepository.GetAsync(shipmentId); 
    var count = shipment.Vehicles.Count; 
    

    Hi, I've tried this and doesn't work. Only work around I have is to add the following which I'd rather not do, which uses what you told me

    public class EfCoreShipmentRepository : EfCoreServiceRepository<CoreServiceDbContext, Shipment, Guid>, IShipmentRepository
    {
        public EfCoreShipmentRepository(IDbContextProvider<CoreServiceDbContext> dbContextProvider)
            : base(dbContextProvider)
        {
        }
    
        public override async Task<Shipment> GetAsync(Guid id, bool includeDetails = true, CancellationToken cancellationToken = default)
        {
            await this.UnitOfWorkManager.Current.SaveChangesAsync(cancellationToken);
            var dbContext = await this.GetDbContextAsync();
            var shipment = await base.GetAsync(id, includeDetails, cancellationToken);
            shipment.Vehicles = await dbContext.Vehicles
                .Where(x => x.ShipmentId == id)
                .ToListAsync(cancellationToken);
            return shipment;
        }
    
    }
    
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Hi, I've tried this and doesn't work.

    Can you share a simple project to show that?

    Thanks.

    liming.ma@volosoft.com

  • User Avatar
    0
    AutosledDBP created

    hi

    Hi, I've tried this and doesn't work.

    Can you share a simple project to show that?

    Thanks.

    liming.ma@volosoft.com

    Hi, sorry for the delay. Sent simple project to show it via email.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Try this:

    private async Task UpdateVehicleCount(Guid shipmentId)
    {
        using (var uow = this.UnitOfWorkManager.Begin(requiresNew: true))
        {
            var shipment = await this.ShipmentRepository.GetAsync(shipmentId);
            shipment.VehicleCount = shipment.Vehicles.Count;
            await this.ShipmentRepository.UpdateAsync(shipment);
    
            await uow.CompleteAsync();
        }
    }
    
  • User Avatar
    0
    AutosledDBP created

    Hi, it does not work either. Won't update count.

    hi

    Try this:

    private async Task UpdateVehicleCount(Guid shipmentId) 
    { 
        using (var uow = this.UnitOfWorkManager.Begin(requiresNew: true)) 
        { 
            var shipment = await this.ShipmentRepository.GetAsync(shipmentId); 
            shipment.VehicleCount = shipment.Vehicles.Count; 
            await this.ShipmentRepository.UpdateAsync(shipment); 
     
            await uow.CompleteAsync(); 
        } 
    } 
    
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Here is my test code:

  • User Avatar
    0
    AutosledDBP created

    Hi here's mine. I'm running the solution and executing the endpoints from the swagger. My database for this test is MS SQL Server. Don't know what else can I supply to further help understand why it's happening on my end.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    add this line await UnitOfWorkManager.Current!.SaveChangesAsync();

Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v9.3.0-preview. Updated on June 13, 2025, 11:37