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?
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();
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.
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.
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 .
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);
}
}
[....]
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();
}
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);
}
}
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();
}
}