2011-07-05 59 views
3

我們有以下內容:使用UserId和User表進行排序。在Entity Framework中附加緩存的斷開連接的實體4.1代碼優先

比方說,我們希望將所有用戶對象緩存爲斷開連接的實體,然後在ASP.NET或Web服務(如環境)中使用它們。

由於環境使用UnitOfWork和IOC框架,我們希望避免任何上下文操作。就像下面的單元測試:

  1. 斷開連接的用戶對象
  2. 創建上下文
  3. 創建訂單對象
  4. 通過用戶對象或id連接用戶
  5. 堅持整個訂單的對象。

經過兩天的使用google搜索,我找不到任何解決方案。

的問題是:

1.用戶對象

如果我們連接的用戶對象(用戶id = 1),默認行爲EF認爲它是一個新用戶對象,並試圖將新的用戶對象保存到數據庫中。這是因爲db已經有userId == 1.

可能的解決方案是重寫DbContext.SaveChanges,試圖找出用戶是否「分離」並強制將其附加到上下文。我想不出如何做到這一點,因爲從SaveChanges調用時擴展方法EFExtensionMethods.IsAttached或EFExtensionMethods.AttachItem認爲T是一個對象。

2. USER_ID

這工作,當你想堅持,並重新加載整個實體之前訪問Order.User對象除外。

最重要的是,我們需要拆分API,以便僅保存使用對象的id(即Order.UserId)而不保存實際對象(Order.User)。重新加載對象後,我們可以同時使用這兩個對象。但我看不到通過API實際執行它的方法。

3 BOTH用戶對象並USER_ID

,即使用戶被標記爲使用用戶ID作爲外鍵這種情況下,EF仍試圖保存用戶對象爲背景擊中1所描述的問題。

看起來像我失去了一些東西(或很多)的原理和問題是:

  • 什麼,你會推薦做?
  • 有來自DbContext.SaveChanges
  • 使EFExtensionMethods.IsAttached工作的很好的和通用的方式是有來自的DbContext使EFExtensionMethods.AttachItem工作的好和通用的方式。SaveChanges

任何幫助,非常感謝。

[TestMethod] 
public void Creating_Order_With_Both_User_And_Id() 
{ 
    int userCount = CountAll<User>(); 
    User user; 
    using (var db = GetContext()) { user = db.Users.AsNoTracking().First(); } 
    using (var db = GetContext()) 
    { 
     var o = CreateOrder(); 
     o.OrderUser = user;   // attach user entity 
     o.UserId = user.UserID;  // attach by id 
     db.Orders.Add(o); 
     db.SaveChanges(); 
    } 
    int newUserCount = CountAll<User>(); 
    Assert.IsTrue(userCount == newUserCount, string.Format("Expected {0} got {1}", userCount, newUserCount)); 
} 

語境和類:

public class User 
{ 
    public User() {} 
    public int UserID {get;set;} 
    public string UserName {get;set;} 
} 

public class Order 
{ 
    public Order() { } 

    public int OrderID { get; set; } 
    public DateTime OrderDate { get; set; } 
    public string OrderName { get; set; } 

    public int UserId { get; set; } 
    public virtual User OrderUser { get; set; } 
} 

public class OrderConfiguration : EntityTypeConfiguration<Order> 
{ 
    public OrderConfiguration() 
    { 
     this.ToTable("ORDERS"); 
     this.Property(x => x.OrderName).HasMaxLength(200); 
     this.HasRequired(u => u.OrderUser).WithMany().HasForeignKey(u => u.UserId); 
    } 
} 

public static class EFExtensionMethods 
{ 
    // does not work, when called from DbContext.SaveChanges thinks T is an Object. 
    public static bool IsAttached<T>(this PPContext db, T entity) where T: class 
    { 
     return db.Set<T>().Local.Any(e => e == entity); 
    } 

    // does not work, when called from DbContext.SaveChanges thinks T is an Object. 
    public static void AttachItem<T>(this PPContext db, T entity) where T: class 
    { 
     db.Set<T>().Attach(entity); 
    } 
} 

public class PPContext : DbContext 
{ 
    public PPContext() : base() { } 
    public PPContext(DbConnection connection) : base(connection, true) { } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     modelBuilder.Configurations.Add(new OrderConfiguration()); 
    } 

    public override int SaveChanges() 
    { 
     var modified = ChangeTracker.Entries().Where(e => e.State == EntityState.Modified || e.State == EntityState.Added); 
     foreach (var item in modified) 
     { 
      ???? 
     } 
    } 

    public DbSet<User> Users {get;set;} 
} 

回答

6

我們已經爲用戶對象添加了標記接口,並將所有ICacheableEntity實體標記爲實體。在SaveChanges中,我們只是將項目標記爲「未更改」。這樣它的工作。

public class User : ICacheableEntity 
{ 
    public User() { } 
    public int UserID {get;set;} 
    public string UserName {get;set;} 
} 

class PPContext 
{ 
    public bool IsSeeding {get;set;} 

    public override int SaveChanges() 
    { 
     var modified = ChangeTracker.Entries().Where(e => e.State == EntityState.Modified || e.State == EntityState.Added); 
     if (!IsSeeding) 
     { 
      foreach (var item in modified.Where(item => (item.Entity is ICacheableEntity))) 
      { 
       item.State = EntityState.Unchanged; 
      } 
     } 
    } 
} 
+0

這是一個很好的解決方案! – Slauma

+0

你所有的緩存項目是不可變的嗎?既然它看起來像緩存的東西永遠不能改變?或者你正在做什麼與IsSeeding標誌? – Jafin

+0

本練習的範圍不考慮緩存的不變性。而我越想它,它就越不重要。真的:) – b0rg

1

是不是有單純的Attach缺少你TestMethod的:

// ... 
using (var db = GetContext()) 
{ 
    var o = CreateOrder(); 

    db.Users.Attach(user); 

    o.OrderUser = user;   // attach user entity 
    o.UserId = user.UserID;  // attach by id 
    db.Orders.Add(o); 
    db.SaveChanges(); 
} 
// ... 

否則EF後,用戶纔會進入Added狀態當您將訂單添加到上下文並將創建一個新用戶,當您撥打SaveChanges

+0

謝謝Slauma。呼叫附加正是我想要避免的。我試圖查看是否有其他可能性,如跟蹤SaveChanges方法中的所有「已添加」實體並強制將它們附加到上下文中。 – b0rg

+0

@ b0rg:嗯,也許你太快接受了我的回答。我不想說除了調用'Attach'之外別無他法。我不知道你想避免這一點。也許對於'SaveChanges'方法中''''''item.State = EntityState.Unchanged;'是可能的。 (但是你必須區分用戶的順序,因爲它們都處於'Added'狀態。)但我不明白你爲什麼要避免'Attach'。這對我來說更容易。 – Slauma

+0

我還是這個新手。你的回答是正確的,沒有簡單的方法。我想爲您的閱讀大量代碼+1努力。避免附加的主要原因,因爲在我們的應用程序中,它不是一個Order和User,而是一些有20+緩存「字典」對象的大對象,並且每次都附上它們都有點無聊...... :) – b0rg

相關問題