Reputation: 2701
In my generic base repository I have the following simple method for removing an entity from database:
public async Task<bool> DeleteAsync(TKey id)
{
var item = await Context.Set<TDb>().FindAsync(id).ConfigureAwait(false);
if (item == null)
return null;
var result = Context.Set<TDb>().Remove(item);
await Context.SaveChangesAsync().ConfigureAwait(false);
return result.State == EntityState.Modified || result.State == EntityState.Deleted;
}
Then in my DB Context I do set shadow properties in my save changes async in a following way (just like Microsoft suggests) (code simplified for better clarity):
public class EfCoreDbContext : DbContext, IUnitOfWork
{
public EfCoreDbContext(
DbContextOptions options
IConfiguration configuration) : base(options)
{
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
ChangeTracker.SetShadowISoftDeletableProperties();
ChangeTracker.SetShadowIUserOwnableProperties(UserResolver);
ChangeTracker.SetShadowIAuditableProperties(UserResolver);
return await base.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
}
}
As visible from SaveChangesAsync
method, I already use shadow properties for other tracking items, such as audits, or user ownership which works without a problem.
Finally, here is the Change Tracker code responsible for setting up soft delete
public static void SetShadowISoftDeletableProperties(this ChangeTracker changeTracker)
{
changeTracker.DetectChanges();
foreach (var entry in changeTracker.Entries())
{
if (typeof(ISoftDeletable).IsAssignableFrom(entry.Entity.GetType()))
{
if (entry.State == EntityState.Deleted)
{
entry.State = EntityState.Modified;
entry.Property("IsDeleted").CurrentValue = true;
}
else if (entry.State == EntityState.Added)
{
entry.Property("IsDeleted").CurrentValue = false;
}
else
{
entry.Property("IsDeleted").CurrentValue = entry.Property("IsDeleted").OriginalValue;
}
}
}
}
Everything works fine, IsDeleted
property is being set successfully in the database; however when I inspect my result.State
, it tells me that entity state is Unchanged
(which causes that my DeleteAsync returns false
), which I don't quite understand. Even in code it is visible, that I change state from Deleted
to Modified
.
I can surely ignore this and instead, return plain true after saving changes (in case of error, it would fail before); yet I am just trying to understand why I am getting this unexpected state result. Any help in respect to this matter would be highly appreciated.
Upvotes: 3
Views: 1766
Reputation: 205579
Added
, Modified
and Deleted
are pending states, indicating what EF Core will do with that entity when SaveChanges{Async}
is called. ChangeTracker.HasChanges()
returns true when there is any entity entry with such state.
By default (acceptAllChangesOnSuccess=true
parameter of SaveChanges{Async}
), after successful SaveChanges
these states are updated to reflect the permanent (database) state of these entities. Added
and Modified
become Unchanged
, Deleted
become Detached
(and entries are removed from the change tracker) and ChangeTracker.HasChanges()
returns false.
You can change that by passing acceptAllChangesOnSuccess=false
to SaveChanges{Async}
, in which case you'll see the pending states, but then you should not forget to call ChangeTracker.AcceptAllChanges()
, otherwise the next SaveChanges{Async}
will try to reapply them in the database (and most likely will fail).
Upvotes: 6