2012-04-04 11 views
48

ho semplice entità:Entity Framework Codice Prima AddOrUpdate metodo inserire i valori duplicati

public class Hall 
{ 
    [Key] 
    public int Id {get; set;} 

    public string Name [get; set;} 
} 

Poi nel metodo Seed che uso AddOrUpdate per popolare tabella:

var hall1 = new Hall { Name = "French" }; 
var hall2 = new Hall { Name = "German" }; 
var hall3 = new Hall { Name = "Japanese" }; 

context.Halls.AddOrUpdate(
    h => h.Name, 
    hall1, 
    hall2, 
    hall3 
); 

Poi corro nella gestione dei pacchetti Console:

Add-Migration Current 
Update-Database 

Va tutto bene: ho tre righe in il tavolo "Hall". Ma se corro nel pacchetto di console di gestione di nuovo Update-Database ho già cinque righe:

Id Name 
1 French 
2 Japaneese 
3 German 
4 French 
5 Japanese 

Perché? Penso che dovrebbe essere di nuovo tre righe, non cinque. Ho provato a usare la proprietà Id invece di Name ma non fa la differenza.

UPDATE:

Questo codice produce lo stesso risultato:

var hall1 = new Hall { Id = 1, Name = "French" }; 
var hall2 = new Hall { Id = 2, Name = "German" }; 
var hall3 = new Hall { Id = 3, Name = "Japanese" }; 

context.Halls.AddOrUpdate(
       h => h.Id, 
       hall1); 

context.Halls.AddOrUpdate(
       h => h.Id, 
       hall2); 

context.Halls.AddOrUpdate(
       h => h.Id, 
       hall3); 

anche io ho l'ultima EntityFramework installato tramite NuGet.

+0

Che cosa succede se si utilizza chiamata separata a 'AddOrUpdate' per ogni record? È abbastanza strano che tu abbia 5 record.Significa che ha funzionato una volta, quindi deve esserci qualcosa di speciale nella tua. –

+0

@Ladislav Mrnka: la stessa cosa per chiamate separate. –

+0

Guarda questo: - http://thedatafarm.com/blog/data-access/take-care-with-ef-4-3-addorupdate-method/ –

risposta

52

Ok stavo sbattere la faccia dalla tastiera per un'ora con questo. Se il campo ID della tua tabella è un campo Identità, non funzionerà, quindi utilizza uno diverso per identifierExpression. Ho usato la proprietà Name e rimosso anche il campo Id dall'inizializzatore new Hall {...}.

Questo tweak per i PO codice ha funzionato per me, quindi spero che aiuta qualcuno:

protected override void Seed(HallContext context) 
{ 
    context.Halls.AddOrUpdate(
     h => h.Name, // Use Name (or some other unique field) instead of Id 
     new Hall 
     { 
      Name = "Hall 1" 
     }, 
     new Hall 
     { 
      Name = "Hall 2" 
     }); 

    context.SaveChanges(); 
} 
+4

Funziona come se fosse progettato per. Se si inserisce un valore in una tabella, SQL assegna il valore successivo nel DB. Se non si desidera che SQL/EF calcoli la prossima chiave da utilizzare, è necessario disattivarla per l'entità. Ho pubblicato una risposta più in basso che spiega questo. –

+0

non ha funzionato per me. Quale versione di EF stai usando? – Campinho

+0

@Campinho EF 4.1 Codice Prima da quello che ricordo –

0

Se l'ID oggetto (hall) è 0, è un inserimento. Penso che sia necessario ricontrollare il campo ID dei tuoi oggetti Hall

+0

Ho aggiornato la mia domanda: il risultato è lo stesso. –

+0

Dovrebbe funzionare, la logica sembra essere corretta. Forse, il commit è mancante, o qualche altro bug nel codice che non stai mostrando. possibilmente creare un timestamp in una nuova colonna per confermare questa situazione. – weeyoung

+0

non c'è nient'altro nel mio codice. –

0

Il campo ID è un campo Identità? Mi stavo imbattendo in questo stesso problema. Quando ho rimosso lo stato Identity dal mio campo ID e ho impostato gli ID nel database, questo ha risolto il problema.

Questo ha funzionato per me, dal momento che queste erano tabelle di ricerca e non avrebbero dovuto essere campi di identità, comunque.

+0

Sì, è un campo identità. Ho aggiunto la condizione se la tabella non è vuota, quindi la tabella dei semi. –

1

Ho trovato che AddOrUpdate funziona bene con campi che non sono ID. Se questo funziona per voi: context.Halls.AddOrUpdate(h => h.Name, hall1, hall2, hall3)

Si consiglia di usare i nomi di Hall come 'French_test_abc_100', 'German_test_abc_100' ecc

che ferma rigido codificato i dati di prova a confondere le cose, quando si sta testando la vostra applicazione.

9

Questo codice funziona:

public Configuration() 
{ 
    AutomaticMigrationsEnabled = true; 
} 

protected override void Seed(HallContext context) 
{ 
    context.Halls.AddOrUpdate(
     h => h.Id, 
     new Hall 
     { 
      Id = 1, 
      Name = "Hall 1" 
     }, 
     new Hall 
     { 
      Id = 2, 
      Name = "Hall 2" 
     }); 

    context.SaveChanges(); 
} 
+5

Potrebbe funzionare, ma non è corretto. ID è generato sul server. Eseguilo e SQL utilizzerà 1 e 2 per i primi due ID. Quindi eliminare quelle righe ed eseguirlo di nuovo, Id = 1, verrà ignorato e SQL utilizzerà ID = 3, 4 quando vengono inseriti successivamente. O cambia l'ID a 99, 100 nel codice sopra. Rilascia il DB, Esegui il metodo seed e non utilizzerà 99, 100, ma SQL utilizzerà 1 e 2. Inoltre, se tali righe di seed vengono eliminate, ogni seed run inserirà di nuovo gli stessi dati perché l'ID non corrisponderà . È molto meglio usare Name come campo unico. – RickAndMSFT

+1

Quindi suggerisci di contrassegnare la risposta di Ciaran Bruen come corretta? Non riesco a controllarlo ora –

3

Ciò può essere causato anche se si sta impostando lo Stato entità in modo non corretto. Continuavo a ricevere il seguente errore quando eseguivo update-database ... "La sequenza contiene più di un elemento corrispondente."

Ad esempio, avevo creato due righe duplicate su ogni comando del database di aggiornamento (che ovviamente non dovrebbe accadere quando si semina dati), e quindi il prossimo comando update-database non funzionerebbe affatto visto che trovato più di una corrispondenza (da qui l'errore di sequenza di dire che ho più di una riga corrispondente). Questo perché avevo ignorato SaveChanges nel mio file di contesto con una chiamata di metodo per ApplyStateChanges ...

public override int SaveChanges() 
{ 
    this.ApplyStateChanges(); 
    return base.SaveChanges(); 
} 

stavo usando ApplyStateChanges per garantire che quando si aggiungono grafici oggetto, Entity Framework sa esplicitamente se l'oggetto si trova in uno stato aggiunto o modificato. L'intera spiegazione su come sto usando ApplyStateChanges può essere trovata here.

E funziona perfettamente (ma l'avvertenza !!) ... se si esegue il seeding del database utilizzando le migrazioni CodeFirst, il metodo sopra causerà il caos per la chiamata AddOrUpdate() all'interno del metodo Seed. Quindi, prima di ogni altra cosa, è sufficiente controllare il file DBContext e assicurarsi di non sovrascrivere SaveChanges nel modo precedente, altrimenti si otterranno dati duplicati che eseguono il comando update-database una seconda volta e quindi non funzioneranno affatto. terza volta poiché c'è più di una riga per ogni oggetto corrispondente.

Quando si tratta di esso, non è necessario configurare l'ID in AddOrUpdate() ... che vanifica l'intero scopo della semina database semplice e iniziale. Funziona bene da qualcosa come:

context.Students.AddOrUpdate(
    p => p.StudentName, 
    new Student { StudentName = "Bill Peters" }, 
    new Student { StudentName = "Jandra Nancy" }, 
    new Student { StudentName = "Rowan Miller" }, 
    new Student { StudentName = "James O'Dalley" }, 

Basta che io non sono l'override del metodo SaveChanges nel mio file di contesto con una chiamata a ApplyStateChanges. Spero che questo ti aiuti.

0

Penso che sia probabile che sia necessario annullare le migrazioni di database esistenti (ad esempio, avviare il database da zero) con qualcosa come "Database di aggiornamento TargetMigration: 0" seguito da "Database di aggiornamento".

Così com'è, non si sta ignorando la tabella oi valori esistenti, si è solo aggiungere/aggiornare tali valori. Questo deve accadere per ottenere il risultato desiderato.

Ecco un buon riferimento per le migrazioni EF: http://elegantcode.com/2012/04/12/entity-framework-migrations-tips/

8

So che questa è una vecchia questione, ma la risposta giusta è che se si sta impostando l'id # te stesso e si desidera utilizzare AddOrUpdate allora avete bisogno di dire EF/SQL che non vuoi che generi il numero ID.

modelBuilder.Entity<MyClass>().Property(p => p.Id) 
    .HasDatabaseGeneratedOption(System.ComponentModel 
    .DataAnnotations.Schema.DatabaseGeneratedOption.None); 

Il lato negativo di questo è che quando si inserisce un nuovo elemento è necessario impostare è Id, quindi se questo è fatto in modo dinamico in fase di esecuzione (invece che dai dati di semi) allora si avrà bisogno di calcolare il prossimo Id. Context.MyClasses.Max(c=>c.Id) + 1 funziona bene.

0

Ho usato il campo ID come Identità/Chiave e aggiungere attributi per non assegnare Id dal server. Questo ha risolto il problema per me.

public class Hall 
{ 
    [Key] 
    [Required] 
    [DatabaseGenerated(DatabaseGeneratedOption.None)] 
    public int Id {get; set;} 

    public string Name [get; set;} 
} 
0

Giusto per la risposta di Ciaren, il codice di seguito della rimodulazione contesto in ModelCreating, mi ha aiutato a risolvere problemi simili. Assicurarsi di modificare "ApplicationContext" nel nome DbContext.

public class ApplicationContext : DbContext, IDbContext 
 
    { 
 
     public ApplicationContext() : base("ApplicationContext") 
 
     { 
 
       
 
     } 
 

 
     protected override void OnModelCreating(DbModelBuilder modelBuilder) 
 
     { 
 
      modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); 
 
      Database.SetInitializer<ApplicationContext>(null); 
 
      base.OnModelCreating(modelBuilder); 
 
     } 
 
    }

Problemi correlati