web-dev-qa-db-fra.com

EntityFramework, Insert s'il n'existe pas, sinon mise à jour

J'ai des pays Entity-Set, reflétant une table de base de données '<' char (2), char (3), nvarchar (50>) dans ma base de données. 

J'ai un analyseur qui renvoie un tableau Pays [] de pays analysés et il est difficile de le mettre à jour correctement. Ce que je veux, c'est: Prenons le tableau des pays, pour ceux qui ne sont pas déjà dans la base de données, insérez-les, et ceux mis à jour si des champs sont différents. Comment cela peut-il être fait?

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


    }
}

Je pensais quelque chose comme 

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

J'espère que tout le monde a déjà eu ce problème et qu'il a une solution pour moi. Si je ne peux pas utiliser l’objet Country et insérer/mettre à jour facilement un tableau d’entre eux, je ne vois pas beaucoup d’avantages à utiliser le cadre. est déjà dans la base de données avant l'insertion?

Solution

Voir la réponse du message à la place.

J'ai ajouté le paramètre override égal à ma classe de pays:

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

et ensuite:

        var data = e.ParsedData as Country[];
        using (var db = new entities())
        {
            foreach (var item in data.Except(db.Countries))
            {
                db.AddToCountries(item); 
            }
            db.SaveChanges();
        }
23
Poul K. Sørensen

Je le ferais directement:

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

Je ne sais pas combien de fois votre application doit exécuter ceci ou combien de pays votre monde a. Mais j’ai le sentiment que ce n’est pas là que vous devez penser à des optimisations sophistiquées des performances.

Modifier

Approche alternative qui n’émettrait qu’une seule requête:

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

La forme moderne, utilisant les versions EF les plus récentes serait:

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

AlreadyExists peut provenir de la vérification de la clé ou de l'interrogation de la base de données pour savoir si l'élément existe déjà à cet endroit.

9
Gábor

Vous pouvez implémenter votre propre IEqualityComparer<Country> et le transmettre à la méthode Except(). En supposant que votre objet Pays ait les propriétés Id et Name, voici un exemple de cette implémentation:

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

et l'utiliser comme 

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

Bien que, dans votre cas, il semble que vous ayez juste besoin d'extraire de nouveaux objets, vous pouvez utiliser var newCountries = data.Where(c => c.Id == Guid.Empty); si votre ID est Guid.

Le meilleur moyen consiste à inspecter la propriété Country.EntityState et à prendre des mesures à partir de là en ce qui concerne la valeur (Détaché, Modifié, Ajouté, etc.)

Vous devez fournir davantage d’informations sur ce que contient votre collection data, c’est-à-dire les objets Pays extraits d’une base de données par le biais du entityframework.

0
Tomislav Markovski

Je ne suis pas sûr que ce sera la meilleure solution, mais je pense que vous devez obtenir tous les pays de la DB, puis la vérifier avec vos données analysées.

 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
Amir Ismail