I am building in a Change History / Audit Log to my MVC app which is using the Entity Framework.
So specifically in the edit method public ActionResult Edit(ViewModel vm)
, we find the object we are trying to update, and then use TryUpdateModel(object)
to transpose the values from the form on to the object that we are trying to update.
I want to log a change when any field of that object changes. So basically what I need is a copy of the object before it is edited and then compare it after the TryUpdateModel(object)
has done its work. i.e.
[HttpPost] public ActionResult Edit(ViewModel vm) { //Need to take the copy here var object = EntityFramework.Object.Single(x=>x.ID = vm.ID); if (ModelState.IsValid) { //Form the un edited view model var uneditedVM = BuildViewModel(vm.ID); //this line seems to confuse the EntityFramework (BuildViewModel() is used to build the model when originally displaying the form) //Compare with old view model WriteChanges(uneditedVM, vm); ... TryUpdateModel(object); } ... }
But the problem is when the code retrieves the "unedited vm", this is causing some unexpected changes in the EntityFramework - so that TryUpdateModel(object);
throws an UpdateException
.
So the question is - in this situation - how do I create a copy of the object
outside of EntityFramework to compare for change/audit history, so that it does not affect or change the EntityFramework at all
edit: Do not want to use triggers. Need to log the username who did it.
edit1: Using EFv4, not too sure how to go about overriding SaveChanges()
but it may be an option
This route seems to be going nowhere, for such a simple requirement! I finally got it to override properly, but now I get an exception with that code:
public partial class Entities { public override int SaveChanges(SaveOptions options) { DetectChanges(); var modifiedEntities = ObjectStateManager.GetObjectStateEntries(EntityState.Modified); foreach (var entry in modifiedEntities) { var modifiedProps = ObjectStateManager.GetObjectStateEntry(entry).GetModifiedProperties(); //This line throws exception The ObjectStateManager does not contain an ObjectStateEntry with a reference to an object of type 'System.Data.Objects.EntityEntry'. var currentValues = ObjectStateManager.GetObjectStateEntry(entry).CurrentValues; foreach (var propName in modifiedProps) { var newValue = currentValues[propName]; //log changes } } //return base.SaveChanges(); return base.SaveChanges(options); } }
IF you are using EF 4 you can subscribe to the SavingChanges
event.
Since Entities
is a partial class you can add additional functionality in a separate file. So create a new file named Entities
and there implement the partial method OnContextCreated
to hook up the event
public partial class Entities { partial void OnContextCreated() { SavingChanges += OnSavingChanges; } void OnSavingChanges(object sender, EventArgs e) { var modifiedEntities = ObjectStateManager.GetObjectStateEntries(EntityState.Modified); foreach (var entry in modifiedEntities) { var modifiedProps = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).GetModifiedProperties(); var currentValues = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).CurrentValues; foreach (var propName in modifiedProps) { var newValue = currentValues[propName]; //log changes } } } }
If you are using EF 4.1 you can go through this article to extract changes
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