2011-08-06 62 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

相关问题