Je crée un historique des modifications/journal d'audit dans mon application MVC qui utilise Entity Framework.
Donc, spécifiquement dans la méthode d'édition public ActionResult Edit(ViewModel vm)
, nous trouvons l'objet que nous essayons de mettre à jour, puis utilisons TryUpdateModel(object)
pour transposer les valeurs du formulaire vers l'objet que nous essayons de mise à jour.
Je souhaite enregistrer une modification lorsqu'un champ de cet objet change. Donc, fondamentalement, j'ai besoin d'une copie de l'objet avant de le modifier, puis de le comparer après que la TryUpdateModel(object)
a fait son travail. c'est à dire.
[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);
}
...
}
Mais le problème est que lorsque le code récupère le "vm non édité", cela provoque des modifications inattendues dans EntityFramework - de sorte que TryUpdateModel(object);
jette un UpdateException
.
La question est donc - dans cette situation - comment créer une copie du object
en dehors de EntityFramework pour comparer l'historique des modifications/audits, afin qu'il n'affecte ni ne modifie le EntityFramework du tout
modifier: Je ne veux pas utiliser de déclencheurs. Besoin de connecter le nom d'utilisateur qui l'a fait.
edit1: En utilisant EFv4, je ne sais pas trop comment contourner SaveChanges()
mais cela peut être une option
Cette route semble aller nulle part, pour une exigence aussi simple! J'ai finalement réussi à le remplacer correctement, mais maintenant je reçois une exception avec ce 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);
}
}
SI vous utilisez EF 4, vous pouvez vous abonner à l'événement SavingChanges
.
Étant donné que Entities
est une classe partielle, vous pouvez ajouter des fonctionnalités supplémentaires dans un fichier séparé. Créez donc un nouveau fichier nommé Entities
et implémentez la méthode partielle OnContextCreated
pour brancher l'événement
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 vous utilisez EF 4.1, vous pouvez passer par cet article pour extraire les modifications
Voir FrameLog , une bibliothèque de journalisation Entity Framework que j'ai écrite à cet effet. Il est open source, y compris pour un usage commercial.
Je sais que vous préférez simplement voir un extrait de code montrant comment procéder, mais pour gérer correctement tous les cas de journalisation, y compris les changements de relation et les changements plusieurs à plusieurs, le code devient assez volumineux. Si tout va bien la bibliothèque sera un bon ajustement pour vos besoins, mais sinon vous pouvez librement adapter le code.
FrameLog peut enregistrer les modifications apportées à toutes les propriétés scalaires et de navigation, et vous permet également de spécifier un sous-ensemble que vous souhaitez enregistrer.
Il y a un article avec une note élevée ici sur le projet de code: Implémentation d'Audit Trail en utilisant Entity Framework . Il semble faire ce que tu veux. J'ai commencé à utiliser cette solution dans un projet. J'ai d'abord écrit des déclencheurs en T-SQL dans la base de données, mais il était trop difficile de les maintenir avec des changements dans le modèle objet qui se produisaient tout le temps.