Base solution for your next web application
Open Closed

Hard delete issue, aspnet zero latest 5.x #5751


User avatar
0
travelsoft created

Hi,

Consider the following code.

            var proposal = await _travelOfferPriceProposalRepository.GetAll()
                                            .Include(b => b.TravelOfferPriceProposalElements).ThenInclude(b => b.TravelElement)
                                            .Include(b => b.TravelOfferPriceProposalGroups).ThenInclude(b => b.TravelOfferPriceProposalElements).ThenInclude(b => b.TravelElement)
                                            .FirstOrDefaultAsync(b => b.Id == id);

			///
			// This code will automatically do hard delete the data we have. Without any notification. 
			// Due to UnitOfWork.
			///
            proposal.TravelOfferPriceProposalElements = proposal.TravelOfferPriceProposalElements
                      .Where(b => b.TravelOfferPriceProposalGroupId == null && b.TravelOfferPriceProposalId == id).ToList();
			///

			///
			// Need to use this instead
            var test = ObjectMapper.Map<TravelOfferPriceProposalDto>(proposal);
            test.TravelOfferPriceProposalElements = test.TravelOfferPriceProposalElements
                .Where(b => b.TravelOfferPriceProposalGroupId == null && b.TravelOfferPriceProposalId == id).ToList();

            return test;

As you can see our software team has made the logical error of filter out data and storing it on the same entity, causing it to delete the actual data from our database instead of just doing a filterd get.

We understand the issue, however the above code causes a hard delete on our records. Despite the fact that TravelOfferPriceProposalElement is a full audited entity. Should that not be a soft delete?

Is this a issue/shortcomming of the ISoftDelete interface combined with EFCore or is this normal behaviour? If this is normal how can we log hard deletes for fullauditedentities on a application level?

We believe our team has made this mistake on multiple appservices.

Thanks in advance for any advice given.


6 Answer(s)
  • User Avatar
    0
    aaron created
    Support Team

    Despite the fact that TravelOfferPriceProposalElement is a full audited entity. Should that not be a soft delete?

    It should.

    Is this a issue/shortcoming of the ISoftDelete interface combined with EF Core or is this normal behaviour? If this is normal how can we log hard deletes for fullauditedentities on a application level?

    It's because EF Core sets entry.State to EntityState.Modified and foreign key to null, and implicitly deletes. It's normal in EF Core, but probably not expected for ISoftDelete concept.

    You can override ApplyAbpConcepts in DbContext with this partial fix that assumes parent entity is not hard deleted:

    protected override void ApplyAbpConcepts(EntityEntry entry, long? userId, EntityChangeReport changeReport)
    {
        HandleImplicitDeleteForSoftDelete(entry);
        base.ApplyAbpConcepts(entry, userId, changeReport);
    }
    
    protected virtual void HandleImplicitDeleteForSoftDelete(EntityEntry entry)
    {
        if (entry.State == EntityState.Modified && entry.Entity is ISoftDelete)
        {
            var foreignKeys = entry.Metadata.GetForeignKeys();
    
            foreach (var foreignKey in foreignKeys)
            {
                foreach (var property in foreignKey.Properties)
                {
                    var propertyEntry = entry.Property(property.Name);
                    if (propertyEntry.OriginalValue != null && propertyEntry.CurrentValue == null)
                    {
                        entry.State = EntityState.Deleted;
                    }
                }
            }
        }
    }
    
  • User Avatar
    0
    travelsoft created

    That totally makes sense! Thank you for your help. We will apply your solution.

  • User Avatar
    0
    timmackey created

    I tried the above solution:

    1. adding the above code to my DbContext class.
    2. delete async as follows:
    using (CurrentUnitOfWork.DisableFilter(AbpDataFilters.SoftDelete))
    {
        await _roomRepository.DeleteAsync(input.Id);   /* input.Id = 6 */
    }
    

    Yet the records are still soft deleted. How can I actually remove records from a table so that, for example, record id = 6 does not exist in the table?

    Hard delete is a non-negotiable requirement for my application. Absent hard delete the database size would explode in a short time, and consist of 95% soft deleted records.

  • User Avatar
    0
    aaron created
    Support Team

    The code above specifically prevents implicit hard delete.

    If you don't ever want to soft delete, then don't implement an ISoftDelete in the first place.

  • User Avatar
    0
    timmackey created

    OK, don't use the above code. Your answer isn't very hepful. I am using the RAD tool to emit code which implements (somewhere, Apb?) soft delete. ISoftDelete does not exist in my app, ergo, I can't not do something that does not exist. Please show by example how to "not implement ISoftDelete" so that I can hard delete a record in a standard ANZ app.

  • User Avatar
    0
    aaron created
    Support Team

    Inherit Entity instead of FullAuditesEntity.