Implementación del registro de auditoría / historial de cambios con MVC y Entity Framework

Estoy construyendo un Registro de historial de cambios / auditoría en mi aplicación MVC que utiliza Entity Framework.

Así que, específicamente en el método de edición public ActionResult Edit(ViewModel vm) , encontramos el objeto que intentamos actualizar y luego usamos TryUpdateModel(object) para transponer los valores desde el formulario al objeto que estamos tratando de actualizar.

Quiero registrar un cambio cuando cambia cualquier campo de ese objeto. Básicamente, lo que necesito es una copia del objeto antes de editarlo y luego compararlo después de que TryUpdateModel(object) haya hecho su trabajo. es decir

 [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); } ... } 

Pero el problema es cuando el código recupera la “vm no editada”, esto está causando algunos cambios inesperados en EntityFramework, de modo que TryUpdateModel(object); arroja una UpdateException .

Entonces, la pregunta es , en esta situación, cómo creo una copia del object fuera de EntityFramework para comparar el historial de cambios / auditorías, para que no afecte o cambie el EntityFramework en absoluto.

editar: No quiero usar disparadores. Necesito ingresar el nombre de usuario que lo hizo.

edit1: Usando EFv4, no estoy muy seguro de cómo sobreponer SaveChanges() pero puede ser una opción


¡Esta ruta parece ir a ninguna parte, por un requisito tan simple! Finalmente lo sobreescribí correctamente, pero ahora recibo una excepción con ese código:

 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); } } 

SI está utilizando EF 4, puede suscribirse al evento SavingChanges .

Como Entities es una clase parcial, puede agregar funcionalidad adicional en un archivo separado. Entonces crea un nuevo archivo llamado Entities y allí implementa el método parcial OnContextCreated para conectar el evento

 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 } } } } 

Si usa EF 4.1, puede revisar este artículo para extraer cambios

Vea FrameLog , una biblioteca de registro de Entity Framework que escribí para este propósito. Es de código abierto, incluso para uso comercial.

Sé que preferiría ver un fragmento de código que muestra cómo hacerlo, pero para manejar correctamente todos los casos de registro, incluidos los cambios de relación y los cambios de muchos a muchos, el código es bastante grande. Esperemos que la biblioteca sea adecuada para sus necesidades, pero si no puede adaptar libremente el código.

FrameLog puede registrar cambios en todas las propiedades escalares y de navegación, y también le permite especificar un subconjunto que le interesa iniciar sesión.

Hay un artículo con una calificación alta aquí en el proyecto de código: Implementación de Audit Trail usando Entity Framework . Parece hacer lo que quieras. Empecé a usar esta solución en un proyecto. Primero escribí desencadenantes en T-SQL en la base de datos, pero era muy difícil mantenerlos con cambios en el modelo de objetos todo el tiempo.