I want to maintain all modified records and deleted records in my project and track their status using a Status column. Below is the code from my first attempt but it seems I cannot cast OriginalValues.ToObject() to DbEntityEntry for obvious reasons. How shoudl I go about accomplishing this?
public override int SaveChanges()
{
var now = DateTime.Now;
var currentUser = HttpContext.Current.User.Identity.Name;
var trackedEntities = ChangeTracker.Entries<ITrackable>();
foreach (var trackedEntity in trackedEntities)
{
switch(trackedEntity.State)
{
case System.Data.EntityState.Added:
trackedEntity.Entity.CreatedDate = now;
trackedEntity.Entity.CreatedBy = currentUser;
trackedEntity.Entity.Status = Status.Active;
break;
case System.Data.EntityState.Modified:
var originalEntity = trackedEntity.OriginalValues.ToObject() as DbEntityEntry<ITrackable>;
originalEntity.Entity.ModifiedDate = now;
originalEntity.Entity.ModifiedBy = currentUser;
originalEntity.Entity.Status = Status.Modified;
var newEntity = trackedEntity.CurrentValues.ToObject() as DbEntityEntry<ITrackable>;
newEntity.State = System.Data.EntityState.Added;
newEntity.Entity.Status = Status.Active;
break;
case System.Data.EntityState.Deleted:
trackedEntity.State = System.Data.EntityState.Modified;
trackedEntity.Entity.Status = Status.Deleted;
break;
}
}
return base.SaveChanges();
}
I am not sure if I understand correctly what you are trying to achieve in the Modified case. But my understanding is that ToObject() creates a completely new object and fills it with the property values from the OriginalValues dictionary. So, originalEntity is not attached to the context and will just be garbage collected without any effect when the variable goes out of scope.
Therefore, don't you need to save the originalEntity as a new added entity to the database to save the last version before the modification is saved?
I would try this:
case System.Data.EntityState.Modified:
var originalEntity = trackedEntity.OriginalValues.ToObject() as ITrackable;
originalEntity.ModifiedDate = now;
originalEntity.ModifiedBy = currentUser;
originalEntity.Status = Status.Modified;
Entry(originalEntity).Status = System.Data.EntityState.Added;
trackedEntity.Entity.Status = Status.Active;
break;
trackedEntity will be saved anyway because it is in state Modified and originalEntity would be created and saved as a new object which represents the state before modification.
I expect that the cast to ITrackable should work because ToObject() creates an object of the type which is represented by the OriginalValues dictionary, and that must be an ITrackable because you filter the ChangeTracker.Entries enumeration by this interface.
The above is untested, I am unsure if it will work.
Edit
The naming above is a bit confusing because originalEntity is an entity in your model while trackedEntity is a DbEntityEntry<T>. Maybe rename that either to originalEntityObject (ugly) or better trackedEntityEntry.
Just to mention: It's absolutely necessary (unless you are using change tracking proxies consequently) that you call ChangeTracker.DetectChanges() before you enter the foreach loop because otherwise the entries are possibly not in the correct final state and you enter wrong case sections or no section at all (when the state is Unchanged but still has changed properties). POCO changes are detected by comparison with the snapshot in OriginalValues and this last comparison is done in base.SaveChanges which is after your loop and too late. (See also "Edit 2" in the answer here: https://stackoverflow.com/a/6157071/270591.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With