2011-08-06 97 views
22

我有一個實體,集國家,反映了我的數據庫的數據庫表 '<' CHAR(2),焦炭(3),爲nvarchar(50>。的EntityFramework,插入如果不存在,否則更新

我有一個解析器,它返回一個解析國家的Country []數組,並且正在以正確的方式更新它的問題。我想要的是:以國家數組爲單位,對於那些尚未在數據庫中的國家插入它們,而那些已有的更新,如果任何領域是不同的。如何才能做到這一點?

void Method(object sender, DocumentLoadedEvent e) 
{ 
    var data = e.ParsedData as Country[]; 
    using(var db = new DataContractEntities) 
    { 
     //Code missing 


    } 
} 

我想這樣

for(var c in data.Except(db.Countries)) but it wount work as it compares on wronge fields. 

希望以前任何人都有過這個問題,並且對我有一個解決方案。如果我不能使用Country對象並插入/更新它們的數組很容易,我沒有看到使用該框架的很多好處,因爲從執行者我認爲它更快地編寫一個自定義的SQL腳本插入它們而不是等檢查一個國家在插入之前已經在數據庫中了?

解決方案

請參閱後的回覆。

我加覆蓋等於我的祖國類:

public partial class Country 
{ 

    public override bool Equals(object obj) 
    { 
     if (obj is Country) 
     { 
      var country = obj as Country; 
      return this.CountryTreeLetter.Equals(country.CountryTreeLetter); 
     } 
     return false; 
    } 
    public override int GetHashCode() 
    { 
     int hash = 13; 
     hash = hash * 7 + (int)CountryTreeLetter[0]; 
     hash = hash * 7 + (int)CountryTreeLetter[1]; 
     hash = hash * 7 + (int)CountryTreeLetter[2]; 
     return hash; 
    } 
} 

,然後做:

 var data = e.ParsedData as Country[]; 
     using (var db = new entities()) 
     { 
      foreach (var item in data.Except(db.Countries)) 
      { 
       db.AddToCountries(item); 
      } 
      db.SaveChanges(); 
     } 
+0

此外,您可以使用新發布的庫,它會自動設置實體圖中所有實體的定義。你可以閱讀[我對類似問題的回答](http://stackoverflow.com/questions/5557829/update-row-if-it-exists-else-insert-logic-with-entity-framework/39609020#39609020) 。 –

回答

24

檢查我會做簡單:

void Method(object sender, DocumentLoadedEvent e) 
{ 
    var data = e.ParsedData as Country[]; 
    using(var db = new DataContractEntities) 
    { 
     foreach(var country in data) 
     { 
      var countryInDb = db.Countries 
       .Where(c => c.Name == country.Name) // or whatever your key is 
       .SingleOrDefault(); 
      if (countryInDb != null) 
       db.Countries.ApplyCurrentValues(country); 
      else 
       db.Countries.AddObject(country); 
     } 
     db.SaveChanges(); 
    } 
} 

我不知道您的應用程序必須多久運行一次或您的世界有多少個國家。但我有這樣的感覺,那就是你不得不考慮複雜的性能優化。

編輯

替代這將只發出一個查詢方法:

void Method(object sender, DocumentLoadedEvent e) 
{ 
    var data = e.ParsedData as Country[]; 
    using(var db = new DataContractEntities) 
    { 
     var names = data.Select(c => c.Name); 
     var countriesInDb = db.Countries 
      .Where(c => names.Contains(c.Name)) 
      .ToList(); // single DB query 
     foreach(var country in data) 
     { 
      var countryInDb = countriesInDb 
       .SingleOrDefault(c => c.Name == country.Name); // runs in memory 
      if (countryInDb != null) 
       db.Countries.ApplyCurrentValues(country); 
      else 
       db.Countries.AddObject(country); 
     } 
     db.SaveChanges(); 
    } 
} 
+0

解決方案很好,你知不知道它是否每次查詢數據庫時都會碰到var countryInDB = ...或者只是第一次。 –

+0

Didnt瞭解ApplyCurrentValues –

+0

@ s093294:每次調用SingleOrDefault時它都會觸發數據庫。 @Miroprocessor的解決方案也很好,它只會觸發DB一次(當調用ToList時)。但是,它可能會加載太多你不需要的國家。例如:如果你的'data'集合只包含少數(比如說2個)國家,而在數據庫中已經有200個國家,'ToList'會加載所有200個,這是很多不必要的開銷。我已經提供了另一個查詢,它只會訪問一次數據庫,請參閱我的編輯。 – Slauma

0

您可以實現自己的IEqualityComparer<Country>並傳遞到Except()方法。假設你的國家對象IdName特性,即實施的一個例子可能看起來像這樣:

public class CountryComparer : IEqualityComparer<Country> 
{ 
    public bool Equals(Country x, Country y) 
    { 
     return x.Name.Equals(y.Name) && (x.Id == y.Id); 
    } 

    public int GetHashCode(Country obj) 
    { 
     return string.Format("{0}{1}", obj.Id, obj.Name).GetHashCode(); 
    } 
} 

和雖然,在你的情況下,它看起來像你只需要使用它作爲

data.Countries.Except<Country>(db, new CountryComparer()); 

提取新對象,如果您的ID爲Guid,則可以使用var newCountries = data.Where(c => c.Id == Guid.Empty);

,最好的辦法是考察Country.EntityState財產和從值有關於(獨立式,修改,添加等)

你需要提供你的data集合包含即是更多信息採取行動通過實體框架從數據庫中檢索國家對象,在這種情況下,可以跟蹤它們的上下文,或者您是否使用其他方式生成它們。

+0

看起來像我的帖子遺漏了那個部分,它的char(2)char(3)和nvarchar(50)。關鍵是char(2)。 –

+0

那麼國家陣列returend我的方法是基於解析器創建的,所以他們都包含一個密鑰,並分離。然後,我想要分離的國家[]和更新如果更新或插入,如果它不存在數據庫中。 –

0

我不知道這將是最好的解決辦法,但我認爲你必須從數據庫得到所有國家,然後用你的分析數據

void Method(object sender, DocumentLoadedEvent e) 
{ 
    var data = e.ParsedData as Country[]; 
    using(var db = new DataContractEntities) 
    { 
     List<Country> mycountries = db.Countries.ToList(); 
     foreach(var PC in data) 
     { 
      if(mycountries.Any(C => C.Name==PC.Name)) 
      { 
      var country = mycountries.Any(C => C.Name==PC.Name); 
      //Update it here 
      } 
      else 
      { 
       var newcountry = Country.CreateCountry(PC.Name);//you must provide all required parameters 
       newcountry.Name = PC.Name; 
       db.AddToCountries(newcountry) 
      } 
     } 
     db.SaveChanges(); 
    } 
    } 
+0

這也是我的結論,並且正如你所說 - 不知道它是否是最好的解決方案。現在世界上沒有那麼多的國家,但如果這是另一個有數百萬行的國家 - 把它們全部取出可能看起來有點過頭。 –

6

現代的形式,使用後EF版本將是:

context.Entry(record).State = (AlreadyExists ? EntityState.Modified : EntityState.Added); 
context.SaveChanges(); 

AlreadyExists可以來自檢查或者通過查詢數據庫來查看該項目是否已經存在。

+3

您的解決方案看起來很有趣。您能否提供更多的上下文或參考這個「現代形式」的完整實施?我真的很感激! – Rymnel

+1

MSDN,例如:https://msdn.microsoft.com/en-us/data/jj592676.aspx(向下滾動至底部以進行此確切操作)。 –

+0

未在一個查詢中完成。需要兩個。 – AjaxLeung

相關問題