Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EntityFramework, Insert if not exist, otherwise update

Tags:

I'm having a Entity-Set Countries, reflecting a database table '<'char(2),char(3),nvarchar(50> in my database.

Im having a parser that returns a Country[] array of parsed countries, and is having issues with getting it updated in the right way. What i want is: Take the array of countries, for those countries not already in the database insert them, and those existing update if any fields is different. How can this be done?

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

I was thinking something like

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

Hope anyone have had this issues before, and have a solution for me. If i cant use the Country object and insert/update an array of them easy, i dont see much benefict of using the framework, as from performers i think its faster to write a custom sql script that inserts them instead of ect checking if an country is already in the database before inserting?

Solution

See answer of post instead.

I added override equals to my country class:

    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;     } } 

and then did:

        var data = e.ParsedData as Country[];         using (var db = new entities())         {             foreach (var item in data.Except(db.Countries))             {                 db.AddToCountries(item);              }             db.SaveChanges();         } 
like image 822
Poul K. Sørensen Avatar asked Aug 06 '11 10:08

Poul K. Sørensen


People also ask

Does Efcore cache data?

The most common data to cache in EF Core is transactional data. This is the frequently changing data that is created at runtime (e.g. customer, accounts, activities, etc.) and you cache it only for a short time during which your application reads it multiple times.

Is Efcore an ORM?

EF Core is an object-relational mapper (ORM). Object-relational mapping is a technique that enables developers to work with data in object-oriented way by performing the work required to map between objects defined in an application's programming language and data stored in relational datasources.

What is the change tracker in Entityframework?

The Change Tracking tracks changes while adding new record(s) to the entity collection, modifying or removing existing entities. Then all the changes are kept by the DbContext level. These track changes are lost if they are not saved before the DbContext object is destroyed.


2 Answers

I would do it straightforward:

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();      } } 

I don't know how often your application must run this or how many countries your world has. But I have the feeling that this is nothing where you must think about sophisticated performance optimizations.

Edit

Alternative approach which would issue only one query:

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();      } } 
like image 106
Slauma Avatar answered Oct 06 '22 20:10

Slauma


The modern form, using later EF versions would be:

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

AlreadyExists can come from checking the key or by querying the database to see whether the item already exists there.

like image 44
Gábor Avatar answered Oct 06 '22 20:10

Gábor