2013-03-28 70 views
3

我想以分離方式使用EF DbContext/POCO實體,即從我的業務層中檢索實體層次結構,進行一些更改,然後將整個層次結構發送回業務層保留回數據庫。每個BLL調用使用DbContext的不同實例。爲了測試這個,我編寫了一些代碼來模擬這樣的環境。使用實體框架分離POCO對象困難時期

首先,我檢索Customer加上相關OrdersOrderLines: -

Customer customer; 
using (var context = new TestContext()) 
{ 
    customer = context.Customers.Include("Orders.OrderLines").SingleOrDefault(o => o.Id == 1); 
} 

接下來我添加了一個新的Order有兩個OrderLines: -

var newOrder = new Order { OrderDate = DateTime.Now, OrderDescription = "Test" }; 
newOrder.OrderLines.Add(new OrderLine { ProductName = "foo", Order = newOrder, OrderId = newOrder.Id }); 
newOrder.OrderLines.Add(new OrderLine { ProductName = "bar", Order = newOrder, OrderId = newOrder.Id }); 
customer.Orders.Add(newOrder); 
newOrder.Customer = customer; 
newOrder.CustomerId = customer.Id; 

最後我堅持的變化(使用新的上下文): -

using (var context = new TestContext()) 
{ 
    context.Customers.Attach(customer); 

    context.SaveChanges(); 
} 

我意識到這最後一部分是不完整的,毫無疑問,我需要在調用SaveChanges()之前更改新實體的狀態。我是否添加或附加客戶?哪些實體需要更改?

之前,我能到這個階段,運行上面的代碼拋出一個異常:

具有相同鍵的對象已經存在於ObjectStateManager。

似乎從沒有明確地設置兩個OrderLine實體的ID來幹,這樣既默認爲0。我認爲這是很好做到這一點作爲EF將自動地處理事情。難道我做錯了什麼?

此外,以這種「分離」方式工作,似乎需要很多工作來建立關係 - 我必須將新訂單實體添加到customer.Orders集合,設置新訂單的Customer屬性,以及其CustomerId屬性。這是正確的方法還是有一個更簡單的方法?

查看自我跟蹤實體會更好嗎?我會在某個地方看到它們被棄用,或者至少不贊成POCO。

回答

3

基本上有2個選項:

A)樂觀。

你可以非常接近你現在正在進行的方式,只需將所有內容都作爲Modified和希望附加。您正在尋找代碼而不是.Attach()的代碼是:

context.Entry(customer).State = EntityState.Modified; 

絕對不直觀。這個奇怪的外觀調用將修改後的分離的(或由您新構建的)對象附加到該對象上。來源:http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx

如果你不確定一個對象是否已經被添加或修改您可以使用的最後一個段的例子:

context.Entry(customer).State = customer.Id == 0 ? 
           EntityState.Added : 
           EntityState.Modified; 

您需要承擔所有對象的這些動作被添加/修改,所以如果這個對象很複雜,並且其他對象需要通過FK關係在數據庫中更新,則還需要設置它們的EntityState。

根據你的情況,你可以讓這些種不關心的寫入使用不同的語境變化更便宜:

public class MyDb : DbContext 
{ 
    . . . 
    public static MyDb CheapWrites() 
    { 
     var db = new MyDb(); 
     db.Configuration.AutoDetectChangesEnabled = false; 
     db.Configuration.ValidateOnSaveEnabled = false; 
     return db; 
    } 
} 

using(var db = MyDb.CheapWrites()) 
{ 
    db.Entry(customer).State = customer.Id == 0 ? 
     EntityState.Added : 
     EntityState.Modified; 
    db.SaveChanges(); 
} 

你基本上只是禁用一些額外的通話EF讓您代表該無論如何你都忽略了結果。

B)悲觀。實際上,您可以查詢數據庫以驗證自上次提取數據以來沒有更改/添加的數據,然後在安全的情況下進行更新。

var existing = db.Customers.Find(customer.Id); 
// Some logic here to decide whether updating is a good idea, like 
// verifying selected values haven't changed, then 

db.Entry(existing).CurrentValues.SetValues(customer); 
+0

這是我想看的東西,但我仍然不明白我得到的異常。我不認爲我必須將主密鑰ID分配給新實體 - 我認爲EF爲我處理了這個問題。或者只有在附帶的情況下? –

+0

好吧,如果你看看我在那裏鏈接的文檔,.Attach()方法適用於現有的NotModified實體。如果您的OrderLines是新對象,則它們不符合這些條件。 NotModified對象的Key(通常是.Id)在進入數據庫的過程中被授予; 0導致異常。所以解決方法是像上面那樣附加它們,這會導致它插入它們而不包含Key,當然,db會爲你設置Key(同樣是.Id),因爲它是一個Identity列。基本上.Attach()有點令人困惑,應該避免。 –