web-dev-qa-db-fra.com

Entity Framework 6: audit / suivi des modifications

J'ai mon projet principal en C #.

Je travaille sur une base de données, où certaines tables ont les colonnes "user_mod" et "date_mod" pour signer qui et quand fait quelques mods et la même chose avec "data_new" et "user_new".

Ma question: existe-t-il un moyen de centraliser cela et d’insérer automatiquement ces données, en créant l’instance de dbContext?

Sinon, j'utiliserai un outil de piste d'audit. J'en ai vu quelques-unes, mais il y a un problème: elles nécessitent toutes du code dans mon modèle. Mais je ne veux pas écrire dans mon modèle, car si je dois le changer, je vais perdre les mods. Est-il possible d'utiliser une piste d'audit pour EF6 sans écrire dans le ou les fichiers de modèle? Comment?

MODIFIER:

Ma tentative de remplacer les 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);
    }
}

UPDATE: J'ai résumé le solution ici .

46
Piero Alberto

Si vous utilisez DbContext de EF6, vous pouvez utiliser ChangeTracker dans SaveChanges override pour rechercher les entités ajoutées/modifiées de type personnalisé, par exemple 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();
}
69
Alaa Masoud
16
Bilal Fazlani

Il existe un moyen de le faire: vous pouvez créer une classe partielle portant le même nom que votre contexte d'objet et implémenter un remplacement de la méthode SaveChanges. Dans cette substitution, vous pouvez examiner toutes les modifications qui seront transmises à la base de données et les traiter.

Vous pouvez les traiter comme bon vous semble. Dans l'exemple suivant, j'ai créé une interface IAutoTimestampEntity contenant une date de création et une date de modification. Tout objet de ce type serait automatiquement mis à jour avec l'heure du changement.

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

Vous pouvez utiliser le même principe ou examiner le type de chaque entité modifiée en détail. J'aime bien l'aspect déclaratif de l'interface. Il vous permet d'exposer explicitement un aspect de l'automatisation au lieu de le faire en silence par la couche EF.

Si vous avez un DbContext au lieu d'un ObjectContext, convertissez votre DbContext en IObjectContextAdapter pour accéder au ObjectStateManager.

5
samy