Note that there are some explanatory texts on larger screens.

plurals
  1. POEntity Framework 4.1 bug in ObjectContext.SavingChanges handling (?)
    primarykey
    data
    text
    <p>I have a problem with something that seems to be a bug in Entity Framework 4.1: I have added a handler on <code>ObjectContext.SavingChanges</code> which updates a property "LastModified" whenever an object is added to or modified in the database. Then I do the following:</p> <ol> <li>Add two objects to the database, and submit (call <code>SaveChanges()</code>)</li> <li>Modify the first object that was added</li> <li>Extract the two objects ordered by LastModified</li> </ol> <p>The resulting objects are returned in the wrong order. Looking at the objects, I can see that the LastModified property has been updated. In other words, the SavingChanges event was fired properly. But looking in the database, the LastModified column has not been changed. That is, there is now a difference between EF's cached objects and the rows in the database.</p> <p>I tried performing the same update to LastModified in an overridden "SaveChanges" method:</p> <pre><code>public override int SaveChanges() { SaveChangesHandler();//updating LastModified property on all objects return base.SaveChanges(); } </code></pre> <p>Doing this caused the database to be updated properly and the queries returned the objects in proper order.</p> <p>Here is an entire test program showing the error:</p> <pre><code>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&lt;Order&gt; 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&lt;Order&gt; GetOldestOrders(int max) { return context.Orders.OrderBy(order =&gt; order.LastModified).Take(max).ToList(); } public void SetUpDatabase() { //Strategy for always recreating the DB every time the app is run. var dropCreateDatabaseAlways = new DropCreateDatabaseAlways&lt;PersistenceContext&gt;(); 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&lt;Order&gt; 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 =&gt; 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); } } } } </code></pre> <p>I should add that the SavingChanges handler worked fine before we upgraded to EF4.1 and using Code-First (that is, it worked in EF4.0 with model-first)</p> <p>The question is: Have I found a bug here, or have I done something wrong?</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload