Entity Framework 6: audit / track changes


Tengo mi proyecto principal en C#.

Trabajo en una base de datos, donde algunas tablas tienen las columnas "user_mod" y "date_mod" para firmar quién y cuándo hizo algunos mods y lo mismo con "data_new" y "user_new".

Mi pregunta: ¿hay una manera de centralizar esto y hacer que estos datos se inserten automáticamente, donde creo la instancia de dbContext?

Si no, usaré una herramienta de seguimiento de auditoría. He visto algunos de estos, pero hay un problema: todos estos, requieren algún código en mi modelo. Pero no quiero escribir en mi modelo, porque si tengo que cambiarlo, perderé los mods. ¿Es posible usar una pista de auditoría para EF6 sin escribir en los archivos del modelo? ¿Cómo?

EDITAR:

Mi intento de anular los SaveChanges.

public partial class PieEntities : DbContext
{
    public override int SaveChanges(System.Data.Objects.SaveOptions options)
    {
        var timestamp = DateTime.Now;

        EntityState es = EntityState.Added;
        ObjectStateManager o = new ObjectStateManager();

        foreach (ObjectStateEntry entry in o.GetObjectStateEntries(EntityState.Added ))  {
            if (entry.Entity.GetType() == typeof(TabImpianti)) {
                TabImpianti impianto = entry.Entity as TabImpianti;
                impianto.DATA_INS = timestamp;
                impianto.DATA_MOD = timestamp;
                string u = mdlImpostazioni.p.UserName;
                impianto.USER_INS = u;
                impianto.USER_MOD = u;
            }
        }
        return base.SaveChanges(options);
    }
}

ACTUALIZACIÓN: He resumido la solución aquí.

Author: Piero Alberto, 2014-10-14

3 answers

Si usa el DbContext de EF6, puede usar ChangeTracker en SaveChanges override para encontrar entidades agregadas/modificadas de tipo personalizado, por ejemplo, IAuditedEntity.

public interface IAuditedEntity {
  string CreatedBy { get; set; }
  DateTime CreatedAt { get; set; }
  string LastModifiedBy { get; set; }
  DateTime LastModifiedAt { get; set; }
}

public override int SaveChanges() {
  var addedAuditedEntities = ChangeTracker.Entries<IAuditedEntity>()
    .Where(p => p.State == EntityState.Added)
    .Select(p => p.Entity);

  var modifiedAuditedEntities = ChangeTracker.Entries<IAuditedEntity>()
    .Where(p => p.State == EntityState.Modified)
    .Select(p => p.Entity);

  var now = DateTime.UtcNow;

  foreach (var added in addedAuditedEntities) {
    added.CreatedAt = now;
    added.LastModifiedAt = now;
  }

  foreach (var modified in modifiedAuditedEntities) {
    modified.LastModifiedAt = now;
  }

  return base.SaveChanges();
}
 65
Author: Alaa Masoud,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-14 09:32:29
 15
Author: Bilal Fazlani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-02 08:52:17

Hay una forma de hacerlo: puede crear una clase parcial que tenga el mismo nombre que su contexto de objeto e implementar una anulación del método SaveChanges. En este override puedes ver todos los cambios que serán empujados a la base de datos y procesarlos.

Puede procesarlos de la manera que desee, en el siguiente ejemplo creé una interfaz IAutoTimestampEntity que contenía una fecha de creación y una fecha de modificación. Cualquier objeto de este tipo se actualiza automáticamente con el tiempo de cambio.

public override int SaveChanges(System.Data.Objects.SaveOptions options)
{
    var timestamp = DateTime.Now;

    foreach (var InsertedAutoTimestampEntity in ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Added).Select(ose => ose.Entity).OfType<IAutoTimestampEntity>())
    {
        InsertedAutoTimestampEntity.CreationDate = timestamp;
        InsertedAutoTimestampEntity.ModificationDate = timestamp;
    }

    foreach (var UpdatedAutoTimestampEntity in ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Modified).Select(ose => ose.Entity).OfType<IAutoTimestampEntity>())
    {
        UpdatedAutoTimestampEntity.ModificationDate = timestamp;
    }

    return base.SaveChanges(options);
}

Puede usar el mismo principio, o puede ver el tipo de cada entidad cambiada en detalles. Sin embargo, me gusta el aspecto declarativo de la interfaz. Le permite exponer un aspecto de la automatización explícitamente en lugar de permitir que la capa EF lo haga silenciosamente.

Si tienes un DbContext en lugar de un ObjectContext, lanza tu DbContext a {[6] } para acceder al ObjectStateManager

 5
Author: samy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-14 08:49:44