2015-07-01 13 views
18

Mi piace il fatto che AddOrUpdate consenta di specificare un filtro da verificare per evitare l'aggiunta di duplicati. Ma vorrei funzionalità simili senza l'aggiornamento.Entity Framework Aggiungi se non esiste senza aggiornamento

In questo momento mi fare qualcosa di simile:

var checkProfile = from p in db.Profile 
    where p => p.LastName == newProfile.lastName 
     && p => p.FirstName == newProfile.firstName 
     && p => p.Middle== newProfile.middle 
    select p; 
if (checkProfile.FirstOrDefault() == null) 
{ 
    db.Profile.Add(newProfile); 
    db.SaveChanges(); 
} 

so di poter fare qualcosa di simile:

db.Profile.AddOrUpdate(p => new {p.LastName, p.FirstName, p.Middle}, newProfile); 
db.SaveChanges(); 

Ma avrei preferito saltare la modifica dei dati in questo caso.

Il primo esempio fa quello che voglio ma con più codice. C'è un modo più semplice/più pulito di fare ciò che voglio nel primo esempio?

Aggiornamento:

Mi piace il suggerimento di Ognyan Dimitrov. Sto cercando di implementarlo. I miei modelli ereditano da BaseEntity. Posso inserire una versione generica di quello lì?

Il mio modello è definito:

public class Address :BaseEntity 
{ 

mio BaseEntity:

public class BaseEntity 
{ 
    public virtual T AddIfNotExists<T>(T entity, Expression<Func<T, bool>> predicate = null) 
    { 
     var exists = predicate != null ? DbSet.Any(predicate) : DbSet.Any(); 
     return !exists ? DbSet.Add(entity) : null; 
    } 
} 

che sto ricevendo errori per qualsiasi (...) e aggiungere (...). L'errore per Aggiungi (...) è 'Un riferimento a un oggetto è richiesto per il campo, metodo o proprietà non statici' System.Data.Entity.DbSet.Add (oggetto) ''

Devo utilizzare this.Add (oggetto)?

Aggiornamento 2:

Ho creato questo codice:

public static class DbSetExtensions 
{ 
    public static T AddIfNotExists<T>(this DbSet<T> dbSet, T entity, Expression<Func<T, bool>> predicate = null) where T : class, new() 
    { 
     var exists = predicate != null ? dbSet.Any(predicate) : dbSet.Any(); 
     return !exists ? dbSet.Add(entity) : null; 
    } 

} 

Ora sto cercando di chiamare in questo modo, ma non è corretto. Perdona la mia mancanza di comprensione.

_db.ProfileIdentifier.AddIfNotExists(newIdentifier, 
      pi => new {pi.ProfileId, pi.ProfileIdentifierTypeId, pi.ProfileIdentifierValue}); 

Aggiornamento - Soluzione:

posso chiamare i DbSetextensions come questo:

_db.ProfileIdentifier.AddIfNotExists(newIdentifier, 
      pi => pi.ProfileId == profileId && 
      pi.ProfileIdentifierTypeId == (int)type && 
      pi.ProfileIdentifierValue == value); 

Grazie mille per lavorare con me Ognyan !!!

+0

Non penso che ci sia - puoi scrivere una stored procedure che farà un 'MERGE', ma non credo che ci sia qualcosa di integrato. –

+0

l'unico modo in cui posso pensare è quello che stai facendo adesso – user1666620

+0

Take cura con il metodo 'AddOrUpdate': http://thedatafarm.com/data-access/take-care-with-ef-4-3-addorupdate-method/ – Colin

risposta

24

Hai provato a verificare se l'entità esiste e in caso contrario - aggiungerla? Come questo:

UPDATE

using System.Linq.Expressions; 
    public class ContextWithExtensionExample 
    { 
     public void DoSomeContextWork(DbContext context) 
     { 
      var uni = new Unicorn(); 
      context.Set<Unicorn>().AddIfNotExists(uni , x => x.Name == "James"); 
     } 
    } 

    public static class DbSetExtensions 
    { 
     public static T AddIfNotExists<T>(this DbSet<T> dbSet, T entity, Expression<Func<T, bool>> predicate = null) where T : class, new() 
     { 
      var exists = predicate != null ? dbSet.Any(predicate) : dbSet.Any(); 
      return !exists ? dbSet.Add(entity) : null; 
     } 
    } 

È possibile utilizzare questo metodo direttamente e ricordarsi di chiamare DbContext.SaveChanges() dopo la chiamata.

+1

Un metodo di estensione è un buon modo per impacchettare questa soluzione. – pseudocoder

+0

Sembra buono. Dove metterei quel codice? Nel mio file DBContext? O il mio file di modello? –

+2

È possibile definire questo metodo in qualsiasi assembly a cui si fa riferimento dal codice chiamante. "I metodi di estensione sono definiti come metodi statici ma sono chiamati usando la sintassi del metodo di istanza." -MSDN https://msdn.microsoft.com/en-us/library/bb383977.aspx – pseudocoder

1

L'unica cosa che viene in mente è usare IEqualityComparer<T>, ma questo non ferma il lavoro, semplicemente lo allontana e crea un codice più pulito.

5

Citato da MSDN EF article.

Inserire o un motivo aggiornamento

un modello comune per alcune applicazioni è o Aggiungi un'entità come nuovo (risultante in un inserto di database) o Allega un'entità come esistente e segnare come modificato (con conseguente in un aggiornamento del database) a seconda di il valore della chiave primaria. Ad esempio, quando si utilizzano le chiavi primarie intere generate dal database , è comune trattare un'entità con una chiave zero come nuova e un'entità con una chiave diversa da zero come esistente. Questo modello può essere ottenuto impostando lo stato dell'entità in base a un controllo del valore della chiave primaria.

Si noti che quando si modifica lo stato di Modificati tutte le proprietà l'ente sarà contrassegnato come modificato e tutti i valori delle proprietà saranno inviati al database quando SaveChanges viene chiamato.

context.Entry(blog).State = blog.BlogId == 0 ? 
          EntityState.Added : 
          EntityState.Modified; 

context.SaveChanges(); 
+1

In realtà, in questo caso, non desidero essere modificato. Non voglio SaveChanges se il record esiste già. –

+0

@ br4d grazie per la risposta - non lo capisco del tutto: da dove viene ottenuta l'istanza del blog? – BKSpurgeon

+0

@BKSpurgeon Probabilmente ottenuto come parametro del metodo. Quale potrebbe essere mappato in modo sicuro da una classe del modello di vista a una classe di entità. Come tale: 'categoria pubblica AddOrUpdate (categoriaVM categoriaVM) { var categoria = categoriaVM.Adapt (); context.Entry (category) .State = category.Id == 0? EntityState.Added: EntityState.Modified; } ' –

1

Quello che ha funzionato per me è questo:

public static void AddIfNotExists<T>(this DbSet<T> dbSet, Func<T, object> predicate, params T [] entities) where T : class, new() 
{ 
    foreach (var entity in entities) 
    { 
     var newValues = predicate.Invoke(entity); 
     Expression<Func<T, bool>> compare = arg => predicate(arg).Equals(newValues); 
     var compiled = compare.Compile(); 
     var existing = dbSet.FirstOrDefault(compiled); 
     if (existing == null) 
     { 
      dbSet.Add(entity); 
     } 
    } 
} 
4

ho usato qualcosa di simile, leggere questi due posti per fare il mio codice. Spero di aiutare chi ha bisogno di una firma simile a AddOrUpdate.

Entity Framework Add if not exist without update

Making AddOrUpdate change only some properties

using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Reflection; 

namespace System.Data.Entity.Migrations 
{ 
    // 
    // Summary: 
    //  Metodos de extensão para System.Data.Entity.IDbSet 
    public static class DbSetMigrationsGustavoExtensions 
    { 
     /// <summary> 
     /// Adiciona uma entidade se ela não existe ainda 
     /// Assinatura semelhante ao AddOrUpdate 
     /// </summary> 
     /// <typeparam name="TEntity"></typeparam> 
     /// <param name="set">Set onde serão adicionadas as entidades</param> 
     /// <param name="identifierExpression">Campos usados na comparação</param> 
     /// <param name="entities">Entidades para adicionar</param> 
     public static void AddIfNotExists<TEntity>(this IDbSet<TEntity> set, Expression<Func<TEntity, object>> identifierExpression, params TEntity[] entities) where TEntity : class 
     { 

      var identifyingProperties = GetProperties<TEntity>(identifierExpression).ToList(); 
      var parameter = Expression.Parameter(typeof(TEntity)); 
      foreach (var entity in entities) 
      { 
       var matches = identifyingProperties.Select(pi => Expression.Equal(Expression.Property(parameter, pi.Name), Expression.Constant(pi.GetValue(entity, null)))); 
       var matchExpression = matches.Aggregate<BinaryExpression, Expression>(null, (agg, v) => (agg == null) ? v : Expression.AndAlso(agg, v)); 

       var predicate = Expression.Lambda<Func<TEntity, bool>>(matchExpression, new[] { parameter }); 
       if (!set.Any(predicate)) 
       { 
        set.Add(entity); 
       } 
      } 
     } 

     private static IEnumerable<PropertyInfo> GetProperties<T>(Expression<Func<T, object>> exp) where T : class 
     { 
      Debug.Assert(exp != null); 
      Debug.Assert(exp.Body != null); 
      Debug.Assert(exp.Parameters.Count == 1); 

      var type = typeof(T); 
      var properties = new List<PropertyInfo>(); 

      if (exp.Body.NodeType == ExpressionType.MemberAccess) 
      { 
       var memExp = exp.Body as MemberExpression; 
       if (memExp != null && memExp.Member != null) 
        properties.Add(type.GetProperty(memExp.Member.Name)); 
      } 
      else if (exp.Body.NodeType == ExpressionType.Convert) 
      { 
       var unaryExp = exp.Body as UnaryExpression; 
       if (unaryExp != null) 
       { 
        var propExp = unaryExp.Operand as MemberExpression; 
        if (propExp != null && propExp.Member != null) 
         properties.Add(type.GetProperty(propExp.Member.Name)); 
       } 
      } 
      else if (exp.Body.NodeType == ExpressionType.New) 
      { 
       var newExp = exp.Body as NewExpression; 
       if (newExp != null) 
        properties.AddRange(newExp.Members.Select(x => type.GetProperty(x.Name))); 
      } 

      return properties.OfType<PropertyInfo>(); 
     } 

     /// <summary> 
     /// Faz um set.Any(predicate) 
     /// Se não existe nada no set então adiciona 
     /// </summary> 
     /// <typeparam name="T"></typeparam> 
     /// <param name="set">Set onde será adicionada a entidade</param> 
     /// <param name="predicate">Condição (exemplo: dbUser => dbUser.Nome == "Gustavo")</param> 
     /// <param name="entity">Entidade para adicionar</param> 
     /// <returns></returns> 
     public static T AddIfNotExists<T>(this IDbSet<T> set, Expression<Func<T, bool>> predicate, T entity) where T : class, new() 
     { 
      return !set.Any(predicate) ? set.Add(entity) : null; 
     } 
    } 
} 
+0

Perché è necessario identificare le Proprietà? Espressione > predicato Da [risposta di Ognyan Dimitrov] (https://stackoverflow.com/questions/31162576/entity-framework-add-if-not-exist-without-update/31162909#31162909) dovrebbe essere sufficiente . –

+0

Ho appena pubblicato il mio codice, perché ha la stessa firma AddOrUpdate AddOrUpdate public static void (questo IDbSet set, Expression > identifierExpression, PARAMS entità TEntity []) dove TEntity: class; –

1

La soluzione è OK, quando si deve aggiungere solo una voce, ma è molto costoso in termini di prestazioni nel caso in cui si devono aggiungere più elementi. Penso che ci sia una soluzione migliore:

public static class DbSetExtensions 
{ 
    public static EntityEntry<TEnt> AddIfNotExists<TEnt, TKey>(this DbSet<TEnt> dbSet, TEnt entity, Func<TEnt, TKey> predicate) where TEnt : class 
    { 
     var exists = dbSet.Any(c => predicate(entity).Equals(predicate(c))); 
     return exists 
      ? null 
      : dbSet.Add(entity); 
    } 

    public static void AddRangeIfNotExists<TEnt, TKey>(this DbSet<TEnt> dbSet, IEnumerable<TEnt> entities, Func<TEnt, TKey> predicate) where TEnt : class 
    { 
     var entitiesExist = from ent in dbSet 
      where entities.Any(add => predicate(ent).Equals(predicate(add))) 
      select ent; 

     dbSet.AddRange(entities.Except(entitiesExist)); 
    } 
} 

Così più tardi può essere utilizzato in questo modo:

using (var context = new MyDbContext()) 
{ 
    var user1 = new User { Name = "Peter", Age = 32 }; 
    context.Users.AddIfNotExists(user1, u => u.Name); 

    var user2 = new User { Name = "Joe", Age = 25 }; 
    context.Users.AddIfNotExists(user2, u => u.Age); 

    // Adds user1 if there is no user with name "Peter" 
    // Adds user2 if there is no user with age 25 
    context.SaveChanges(); 
} 

Così più tardi può essere utilizzato in questo modo:

using (var context = new MyDbContext()) 
{ 
    var user1 = new User { Name = "Peter", Age = 32 }; 
    context.Users.AddIfNotExists(user1, u => u.Name); 

    var user2 = new User { Name = "Joe", Age = 25 }; 
    context.Users.AddIfNotExists(user2, u => u.Age); 

    // Adds user1 if there is no user with name "Peter" 
    // Adds user2 if there is no user with age 25 
    context.SaveChanges(); 
} 

Così più tardi può essere utilizzato in questo modo:

using (var context = new MyDbContext()) 
{ 
    var user1 = new User { Name = "Peter", Age = 32 }; 
    context.Users.AddIfNotExists(user1, u => u.Name); 

    var user2 = new User { Name = "Joe", Age = 25 }; 
    context.Users.AddIfNotExists(user2, u => u.Age); 

    // Adds user1 if there is no user with name "Peter" 
    // Adds user2 if there is no user with age 25 
    context.SaveChanges(); 
} 
Problemi correlati