5
votes

In my many-to-one relationship I am trying to delete one of the child objects and keep an audit trail in my overridden SaveChanges.

The file.Entity.Product is not null when doing an EntityState.Modified or EntityState.Added but when doing a .Deleted EF seems to be aggressively removing the relationship from the entity even before base.Savechanges() is called.

Is there any way to retrieve when Product this file was associated with in my SaveChanges override after I have called .Remove on the File (child)?

var files = from e in ChangeTracker.Entries<SavedFile>()
                    where e.State != EntityState.Unchanged
                    select e;
if (file.State == EntityState.Deleted)
            {
                var text = "File Deleted: " + file.Entity.FriendlyFileName;
                // file.Entity.Product is null
                var updatedProduct = new Update { Product = file.Entity.Product, UpdateDateTime = DateTime.Now, UpdateText = text, User = HttpContext.Current.Request.LogonUserIdentity.Name };
                Updates.Add(updatedProduct);
            }
4
I'm guessing there is a foreach loop over files assigning the value of file, right? - Shago
you can store the required fields in properties and mark it NotMapped or Ingnore if you are using fluid mapping - user5135401

4 Answers

2
votes

Just as you find the file in the ChangeTracker's entries, you can find its product, assuming that File has a foreign key association with Product, i.e. it has a ProductID property besides a Product navigation property:

if (file.State == EntityState.Deleted)
{
    var text = "File Deleted: " + file.Entity.FriendlyFileName;
    var productEntry = ChangeTracker.Entries<Product>()
                                    .FirstOrDefault(p => p.Entity.ID == file.ProductID);
    if (productEntry != null)
    {
        var updatedProduct = new Update { Product = productEntry.Entity, UpdateDateTime = DateTime.Now, UpdateText = text, User = HttpContext.Current.Request.LogonUserIdentity.Name };
        Updates.Add(updatedProduct);
    }
}

It's expected behavior that EF doesn't show deleted entities whenever entities are queries from the local cache (e.g. DbSet.Local). So it also has to break associations to prevent these entities from appearing in navigation properties. If you delete a file, EF won't display it in context.Files.Local, but neither will it be visible in a navigation property like product.Files (and neither will the other side of the association, file.Product).

1
votes

I think that there is no way to access the relation ship after deletion

but, why don't you use the soft delete, which is the best practice in my opinion, at any time you can return back the deleted record if was deleted mistakenly ( by marking IsDeleted = false, by the admin with authority)

hope this will help you

0
votes

If you need it for just the Product property, you could capture it with some custom logic on its definition:

class File {
   ...
   private Product product;
   private Product lastProduct;

   public Product Product {
      get { return product; }
      set {
          if (product == value) return;
          lastProduct = Product;
          product = value;
      }
   }

   [NotMapped]
   public Product LastProduct {
      get { return lastProduct; }
   }
   ...
}
0
votes

Answer provided by Gert Arnold should work - but not sure whether it makes sense to check FirstOrDefault(p => p.State != EntityState.Unchanged as the related entity might be still in Unchanged status.

If nothing works, try reloading the entity in the overridden SaveChanges method.

this.Entry((file.Entity as <<Your Entity Type>>)).Reload();
//do your operation/audit log
//then reset the Entity State to Deleted
this.Entry((file.Entity)).State = EntityState.Deleted;

Edit: or explicit load just the required related entities alone

this.Entry(file.Entity as <Type>).Reference(e=> e.Product).Load();

This would end up in an extra db hit while Reload.

Edit: Another option, again if no other good options found

How about executing raw SQL insert query in this scenario to populate audit table?