2011-05-04 64 views
1

我在Entity Framework 4.1中遇到了一個似乎是錯誤的問題:我在ObjectContext.SavingChanges上添加了一個處理程序,它在每次對象被更新時更新屬性「LastModified」添加到數據庫中或在數據庫中修改。然後,我做到以下幾點:實體框架4.1 ObjectContext.SavingChanges中的錯誤處理(?)

  1. 添加兩個對象到數據庫,並提交(調用SaveChanges()
  2. 修改已添加
  3. 提取由上次更改
下令兩個對象的第一個對象

生成的對象以錯誤的順序返回。看着對象,我可以看到LastModified屬性已經更新。換句話說,SavingChanges事件被正確觸發。但是查看數據庫,LastModified列沒有被更改。也就是說,現在EF的緩存對象與數據庫中的行之間存在差異。

我試圖在重寫「的SaveChanges」方法執行相同的更新到上次更改時間:

public override int SaveChanges() 
{ 
    SaveChangesHandler();//updating LastModified property on all objects 
    return base.SaveChanges(); 
} 

這樣做造成的數據庫中進行適當的更新和查詢依次返回的對象。

這裏是一個完整的測試程序顯示錯誤:

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

我要補充一點,之前我們升級到EF4.1並使用代碼優先(也就是SavingChanges處理工作得很好,它在工作EF4 .0與模型優先)

問題是:我在這裏發現了一個錯誤,或者我做錯了什麼?

回答

2

我不確定這是否可以被視爲一個錯誤。看起來會發生的是,您操縱LastModified屬性的方式不會觸發INotifyPropertyChanged,因此更改不會填充到數據庫中。

爲了證明這一點使用:

order2.Name = "modified order2"; 
    ((IObjectContextAdapter)context).ObjectContext.ObjectStateManager.GetObjectStateEntry(order2).SetModifiedProperty("LastModified"); 

要利用這些知識在你SavingChangesHandler:

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); 
    if (entry.State == EntityState.Modified) 
    { 
     ((IObjectContextAdapter) this).ObjectContext.ObjectStateManager.GetObjectStateEntry(entry.Entity).SetModifiedProperty("LastModified"); 
    } 
    } 
} 

編輯:

我看着這個多一點,你是正確的。出於某種原因,MS決定不再使用PropertyInfo.SetValue時觸發PropertyChanged事件。只有一種方法可以確定這是一個錯誤還是一個設計決定:編寫錯誤報告/發佈到msdn論壇。

雖然通過CurrentValue的直接更改屬性,似乎很好地工作:

private static void SetModifiedDate(DateTimeOffset now, DbEntityEntry modifiedEntity) 
{ 
    if (modifiedEntity.Entity == null) 
    { 
    return; 
    } 

    modifiedEntity.Property("LastModified").CurrentValue = now; 
} 
+0

謝謝您的回答。你的建議實際上奏效了!但我認爲它看起來有點像黑客 - 當我修改一個屬性時,不應該由框架自動處理這個問題嗎?正如我在我的問題中寫的,相同的代碼在EF4.0中工作正常。 – JRV 2011-05-05 06:14:26

+0

@JRV看看我的編輯 – Till 2011-05-05 09:52:11

+0

很好 - 再次感謝。這是一個更清潔的方法,我會爲此而努力。我只會接受PropertyInfo.SetValue不起作用的事實,現在我知道了另一種方法來做到這一點:-) – JRV 2011-05-06 05:41:36