2013-11-21 34 views
2

我試圖爲EntityFramework創建一個通用的CreateOrUpdate函數,其中我的類/表總是有一個ID字段。由於該數據發送到網頁並回發到服務器,我完全與上下文斷開連接,並且必須使用標準(set.Any/set.FirstOrDefault)方法來檢查我的對象是否已經存在。如何阻止EntityFramework獲取所有行

該方法在完成工作和根據需要創建/更新方面完美工作。但我發現的是db.Set<T>().FirstOrDefault(whereFunction);從DB中取回所有數據,然後在內存中執行FirstOrDefault。我更喜歡這種情況發生在SQL中,但無論我嘗試過什麼,我都無法使其工作。

對於如何將FirstOrDefault函數轉換爲正確的SQL,您有任何建議嗎?這樣我就不會從數據庫中檢索太多東西了嗎?

此外,我試過First,Any & Count,所有這些都從數據庫中獲取所有行。

public void CreateOrUpdateEntity<T>(T entity) where T : class 
{ 
    using (var db = new ProjectContext()) 
    { 
     Func<T, bool> whereFunction = m => m.As<dynamic>().ID == entity.As<dynamic>().ID; 
     var firstValue = db.Set<T>().FirstOrDefault(whereFunction); 

     if (firstValue == null) 
     { 
      db.Set<T>().Attach(entity); 
      db.ChangeTracker.Entries<T>().First(e => e.Entity == entity).State = EntityState.Added; 
     } 
     else 
     { 
      db.ChangeTracker.Entries<T>().First(e => e.Entity == entity).State = EntityState.Modified; 
     } 
    } 
} 

回答

4

您應該使用expression而不是Func<T, bool>委託。委託的使用調用在內存中執行的Enumerable.FirstOrDefault(Func<T,TResult>)方法(並要求將所有數據加載到客戶端),而不是在服務器端將其翻譯爲SQL並執行的Queryable.FirstOrDefault(Expression<Func<T, TResult>>)

注意:我對你的委託是否可以被翻譯成SQL存有懷疑。

BTW:爲了通過ID來獲得實體您可以使用方法Find

var firstValue = db.Set<T>().Find(entity.As<dynamic>().ID); 
+1

我不得不來測試你的建議,但您的兩個建議,使總的感覺!感覺有點密集,因爲我沒有發現它!我認爲這是EF而不是我的失敗! :) 謝謝! – Faraday

+2

@Vijay還有一個想法 - 如果你所有的實體都有ID屬性,那麼考慮創建基本實體類'public abstract class Entity {public int ID {get;組; }}'爲他們,並用'where T:Entity'參數化你的方法,因此你可以避免動態類型。當然,這隻有在所有實體都具有相同類型的ID的情況下才有意義。 –

+1

好的想法。我原本打算這樣做,但有很多不同的類型。我想我可以不那麼懶惰,爲每個人編寫解決方案......當我有時間看看它是否值得時,我會做一些基準測試。我知道動態不是太好,我傾向於在可能的情況下避免它們......再次感謝您的建議,非常感謝! – Faraday