J'ai un problème avec quelque chose qui semble être un bug dans Entity Framework 4.1: J'ai ajouté un gestionnaire sur ObjectContext.SavingChanges
qui met à jour une propriété "LastModified" à chaque fois qu'un objet est ajouté à ou modifié dans la base de données. Ensuite, je fais ce qui suit:Entity Framework 4.1 bug dans ObjectContext.SavingChanges manutention (?)
- Ajouter deux objets à la base de données, et soumettre (appel
SaveChanges()
) - Modifier le premier objet qui a été ajouté
- Extrait les deux objets commandés par LastModified
Les objets résultants sont renvoyés dans le mauvais ordre. En regardant les objets, je peux voir que la propriété LastModified a été mise à jour. En d'autres termes, l'événement SavingChanges a été déclenché correctement. Mais en regardant dans la base de données, la colonne LastModified n'a pas été modifiée. C'est-à-dire qu'il existe maintenant une différence entre les objets mis en cache d'EF et les lignes de la base de données.
J'ai essayé d'effectuer la même mise à jour LastModified dans une méthode « SaveChanges » surchargée:
public override int SaveChanges()
{
SaveChangesHandler();//updating LastModified property on all objects
return base.SaveChanges();
}
Faire cela a provoqué la base de données mise à jour correctement et les requêtes ont retourné les objets dans le bon ordre.
Voici un programme de test complet montrant l'erreur:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Reflection;
using System.Threading;
namespace TestApplication
{
class Program
{
private PersistenceContext context;
private static void Main(string[] args)
{
var program = new Program();
program.Test();
}
public void Test()
{
SetUpDatabase();
var order1 = new Order {Name = "Order1"};
context.Orders.Add(order1);
var order2 = new Order {Name = "Order2"};
context.Orders.Add(order2);
context.SaveChanges();
Thread.Sleep(1000);
order1 = GetOrder(order1.Id); // Modified 1.
order1.Name = "modified order1";
context.SaveChanges();
List<Order> orders = GetOldestOrders(1);
AssertEquals(orders.First().Id, order2.Id);//works fine - this was the oldest object from the beginning
Thread.Sleep(1000);
order2 = GetOrder(order2.Id); // Modified 2.
order2.Name = "modified order2";
context.SaveChanges();
orders = GetOldestOrders(1);
AssertEquals(orders.First().Id, order1.Id);//FAILS - proves that the database is not updated with timestamps
}
private void AssertEquals(long id1, long id2)
{
if (id1 != id2) throw new Exception(id1 + " != " + id2);
}
private Order GetOrder(long id)
{
return context.Orders.Find(id);
}
public List<Order> GetOldestOrders(int max)
{
return context.Orders.OrderBy(order => order.LastModified).Take(max).ToList();
}
public void SetUpDatabase()
{
//Strategy for always recreating the DB every time the app is run.
var dropCreateDatabaseAlways = new DropCreateDatabaseAlways<PersistenceContext>();
context = new PersistenceContext();
dropCreateDatabaseAlways.InitializeDatabase(context);
}
}
////////////////////////////////////////////////
public class Order
{
public virtual long Id { get; set; }
public virtual DateTimeOffset LastModified { get; set; }
public virtual string Name { get; set; }
}
////////////////////////////////////////////////
public class PersistenceContext : DbContext
{
public DbSet<Order> Orders { get; set; }
public PersistenceContext()
{
Init();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
public void Init()
{
((IObjectContextAdapter) this).ObjectContext.SavingChanges += SavingChangesHandler;
Configuration.LazyLoadingEnabled = true;
}
private void SavingChangesHandler(object sender, EventArgs e)
{
DateTimeOffset now = DateTimeOffset.Now;
foreach (DbEntityEntry entry in ChangeTracker.Entries()
.Where(entity => entity.State == EntityState.Added || entity.State == EntityState.Modified))
{
SetModifiedDate(now, entry);
}
}
private static void SetModifiedDate(DateTimeOffset now, DbEntityEntry modifiedEntity)
{
if (modifiedEntity.Entity == null)
{
return;
}
PropertyInfo propertyInfo = modifiedEntity.Entity.GetType().GetProperty("LastModified");
if (propertyInfo != null)
{
propertyInfo.SetValue(modifiedEntity.Entity, now, null);
}
}
}
}
Je dois ajouter que le gestionnaire SavingChanges a bien fonctionné avant nous sommes passés à EF4.1 et en utilisant le code-première (qui est, il a travaillé dans EF4 .0 avec le modèle-premier)
La question est: Ai-je trouvé un bug ici, ou ai-je fait quelque chose de mal?
Merci pour votre réponse. Votre suggestion a réellement fonctionné! Mais je pense qu'il ressemble un peu à un hack - ne devrait-il pas être géré automatiquement par le cadre lorsque je modifie une propriété? Comme je l'ai écrit dans ma question, le même code a bien fonctionné dans EF4.0. – JRV
@JRV Jetez un oeil à mon édition ci-dessus – Till
Great - merci encore. C'est une approche beaucoup plus propre, et je vais y aller.Je vais juste accepter le fait que PropertyInfo.SetValue ne fonctionne plus, maintenant que je connais l'autre façon de le faire :-) – JRV