web-dev-qa-db-fra.com

Mettre à jour la ligne si elle existe, sinon Insérer une logique avec Entity Framework

Quelqu'un a-t-il des suggestions sur le moyen le plus efficace d'implémenter la logique «update row» s'il existe, insérez-la »à l'aide d'Entity Framework?

146
Jonathan Wood

Si vous travaillez avec un objet attaché (objet chargé à partir de la même instance du contexte), vous pouvez simplement utiliser:

if (context.ObjectStateManager.GetObjectStateEntry(myEntity).State == EntityState.Detached)
{
    context.MyEntities.AddObject(myEntity);
}

// Attached object tracks modifications automatically

context.SaveChanges();

Si vous pouvez utiliser n'importe quelle connaissance sur la clé de l'objet, vous pouvez utiliser quelque chose comme ceci:

if (myEntity.Id != 0)
{
    context.MyEntities.Attach(myEntity);
    context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified);
}
else
{
    context.MyEntities.AddObject(myEntity);
}

context.SaveChanges();

Si vous ne pouvez pas décider de l'existence de l'objet par son ID, vous devez exécuter une requête de recherche:

var id = myEntity.Id;
if (context.MyEntities.Any(e => e.Id == id))
{
    context.MyEntities.Attach(myEntity);
    context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified);
}
else
{
    context.MyEntities.AddObject(myEntity);
}

context.SaveChanges();
147
Ladislav Mrnka

Depuis Entity Framework 4.3, il existe une méthode AddOrUpdate dans l'espace de noms System.Data.Entity.Migrations:

public static void AddOrUpdate<TEntity>(
    this IDbSet<TEntity> set,
    params TEntity[] entities
)
where TEntity : class

qui par le doc :

Ajoute ou met à jour des entités par clé lorsque SaveChanges est appelé. Équivalent à une opération "upsert" à partir de la terminologie de la base de données. Cette méthode peut être utile lors de l'ensemencement des données avec Migrations.


Pour répondre au commentaire de @ Smashing1978 , je vais coller les parties pertinentes du lien fourni par @Colin.

AddOrUpdate a pour tâche de s’assurer que vous ne créez pas de doublons lorsque vous semez des données au cours du développement.

Tout d'abord, il va exécuter une requête dans votre base de données à la recherche d'un enregistrement où tout ce que vous avez fourni comme clé (premier paramètre) correspond au valeur de colonne mappée (ou valeurs) fournie dans AddOrUpdate. Donc ça est un peu lâche pour l'harmonisation mais parfaitement pour l'ensemencement données de temps de conception.

Plus important encore, si une correspondance est trouvée, la mise à jour mettra à jour tout et annulez tout ce qui n’était pas dans votre AddOrUpdate.

Cela dit, je tire des données d'un service externe et insère ou met à jour des valeurs existantes par clé primaire (et mes données locales pour les consommateurs sont en lecture seule) - j'utilise AddOrUpdate en production depuis plus de 6 mois et jusqu'ici pas de problèmes. 

29
Erki M.

Si vous savez que vous utilisez le même contexte et que vous ne détachez aucune entité, vous pouvez créer une version générique comme celle-ci:

public void InsertOrUpdate<T>(T entity, DbContext db) where T : class
{
    if (db.Entry(entity).State == EntityState.Detached)
        db.Set<T>().Add(entity);

    // If an immediate save is needed, can be slow though
    // if iterating through many entities:
    db.SaveChanges(); 
}

db peut bien sûr être un champ de classe, ou la méthode peut être rendue statique et une extension, mais ce sont les bases.

7
ciscoheat

La magie se produit lorsque vous appelez SaveChanges() et dépend de la EntityState actuelle. Si l'entité a un EntityState.Added, il sera ajouté à la base de données. S'il a un EntityState.Modified, il sera mis à jour dans la base de données. Vous pouvez donc implémenter une méthode InsertOrUpdate() comme suit:

public void InsertOrUpdate(Blog blog) 
{ 
    using (var context = new BloggingContext()) 
    { 
        context.Entry(blog).State = blog.BlogId == 0 ? 
                                   EntityState.Added : 
                                   EntityState.Modified; 

        context.SaveChanges(); 
    } 
}

En savoir plus sur EntityState

Si vous ne pouvez pas vérifier sur Id = 0 pour déterminer s'il s'agit d'une nouvelle entité ou non, vérifiez la réponse de Ladislav Mrnka .

5
Stacked

La réponse de Ladislav était proche mais j'ai dû faire quelques modifications pour que cela fonctionne dans EF6 (base de données en premier). J'ai étendu mon contexte de données avec ma méthode sur AddOrUpdate et jusqu'à présent, cela semble bien fonctionner avec des objets détachés:

using System.Data.Entity;

[....]

public partial class MyDBEntities {

  public void AddOrUpdate(MyDBEntities ctx, DbSet set, Object obj, long ID) {
      if (ID != 0) {
          set.Attach(obj);
          ctx.Entry(obj).State = EntityState.Modified;
      }
      else {
          set.Add(obj);
      }
  }
[....]
4
cdonner

Insérer sinon mettre à jour les deux 

public void InsertUpdateData()
{
//Here TestEntities is the class which is given from "Save entity connection setting in web.config"
TestEntities context = new TestEntities();

var query = from data in context.Employee
            orderby data.name
            select data;

foreach (Employee details in query)
{
    if (details.id == 1)
    {
        //Assign the new values to name whose id is 1
        details.name = "Sanjay";
        details. Surname="Desai";
        details.address=" Desiwadi";
    }
    else if(query==null)
    {
        details.name="Sharad";
        details.surname=" Chougale ";
        details.address=" Gargoti";
    }
}

//Save the changes back to database.
context.SaveChanges();
}
2
Sharad Chougale

Alternative pour @LadislavMrnka answer. Ceci si pour Entity Framework 6.2.0.

Si vous avez une DbSet spécifique et un élément qui doit être mis à jour ou créé:

var name = getNameFromService();

var current = _dbContext.Names.Find(name.BusinessSystemId, name.NameNo);
if (current == null)
{
    _dbContext.Names.Add(name);
}
else
{
    _dbContext.Entry(current).CurrentValues.SetValues(name);
}
_dbContext.SaveChanges();

Toutefois, cela peut également être utilisé pour une DbSet générique avec une seule clé primaire ou une clé primaire composite.

var allNames = NameApiService.GetAllNames();
GenericAddOrUpdate(allNames, "BusinessSystemId", "NameNo");

public virtual void GenericAddOrUpdate<T>(IEnumerable<T> values, params string[] keyValues) where T : class
{
    foreach (var value in values)
    {
        try
        {
            var keyList = new List<object>();

            //Get key values from T entity based on keyValues property
            foreach (var keyValue in keyValues)
            {
                var propertyInfo = value.GetType().GetProperty(keyValue);
                var propertyValue = propertyInfo.GetValue(value);
                keyList.Add(propertyValue);
            }

            GenericAddOrUpdateDbSet(keyList, value);
            //Only use this when debugging to catch save exceptions
            //_dbContext.SaveChanges();
        }
        catch
        {
            throw;
        }
    }
    _dbContext.SaveChanges();
}

public virtual void GenericAddOrUpdateDbSet<T>(List<object> keyList, T value) where T : class
{
    //Get a DbSet of T type
    var someDbSet = Set(typeof(T));

    //Check if any value exists with the key values
    var current = someDbSet.Find(keyList.ToArray());
    if (current == null)
    {
        someDbSet.Add(value);
    }
    else
    {
        Entry(current).CurrentValues.SetValues(value);
    }
}
0
Ogglas

Vérifiez la ligne existante avec Tous.

    public static void insertOrUpdateCustomer(Customer customer)
    {
        using (var db = getDb())
        {

            db.Entry(customer).State = !db.Customer.Any(f => f.CustomerId == customer.CustomerId) ? EntityState.Added : EntityState.Modified;
            db.SaveChanges();

        }

    }
0
Ali Osman Yavuz