2012-12-08 6 views
17

Sto utilizzando il primo sistema EF5 basato su DBContext.AddOrUpdate funziona come previsto e produce duplicati

In DbMigrationsConfiguration.Seed Sto cercando di riempire DB con dati fittizi predefiniti. Per eseguire questa operazione, utilizzo il metodo DbSet.AddOrUpdate.

Il codice più semplice per illustrare il mio obiettivo:

j = 0; 

var cities = new[] 
    { 
     "Berlin", 
     "Vienna", 
     "London", 
     "Bristol", 
     "Rome", 
     "Stockholm", 
     "Oslo", 
     "Helsinki", 
     "Amsterdam", 
     "Dublin" 
    }; 
var cityObjects = new City[cities.Length]; 


foreach (string c in cities) 
{ 
    int id = r.NextDouble() > 0.5 ? 0 : 1; 
    var city = new City 
     { 
      Id = j, 
      Name = c, 
      Slug = c.ToLowerInvariant(), 
      Region = regions[id], 
      RegionId = regions[id].Id, 
      Reviewed = true 
     }; 
    context.CitySet.AddOrUpdate(cc => cc.Id, city); 
    cityObjects[j] = city; 
    j++; 
} 

Ho provato ad utilizzare/omettere Id campo così come di utilizzare Id/Slug proprietà come selettore di aggiornamento.

quando viene eseguito Update-Database, il campo Id viene ignorato e il valore viene generato automaticamente da SQL Server e il DB viene riempito con i duplicati; Il selettore Slug consente duplicati e nelle esecuzioni successive produce eccezioni (Sequence contains more than one element).

È il metodo AddOrUpdate destinato a funzionare in questo modo? Devo eseguire upsert a mano?

risposta

26

Prima (nessuna risposta), AddOrUpdate può essere chiamato con una serie di nuovi oggetti, quindi è sufficiente creare una matrice di tipo City[] e chiamare context.CitySet.AddOrUpdate(cc => cc.Id, cityArray); una volta.

(a cura)

In secondo luogo, AddOrUpdate usa l'espressione identificativo (cc => cc.Id) per trovare le città con lo stesso Id di quelli della matrice. Queste città saranno aggiornate. Verranno inserite le altre città nell'array, ma i loro valori Id verranno generati dal database, poiché Id è una colonna Identity. Non può essere impostato da un'istruzione di inserimento. (A meno che non si imposti Identity Insert on). Pertanto, quando si utilizza AddOrUpdate per le tabelle con colonne Identity, si dovrebbe trovare un altro modo per identificare i record perché i valori Id dei record esistenti sono imprevedibili.

Nel tuo caso hai utilizzato Slug come identificatore per AddOrUpdate, che dovrebbe essere univoco (come da tuo commento). Non mi è chiaro il motivo per cui questo non aggiorna i record esistenti con i corrispondenti Slug s.

ho creato un piccolo test: aggiungere o aggiornare un ente con un ID (iedntity) e un nome univoco:

var n = new Product { ProductID = 999, ProductName = "Prod1", UnitPrice = 1.25 }; 
Products.AddOrUpdate(p => p.ProductName, n); 
SaveChanges(); 

Quando "Prod1" non è ancora lì, è inserita (ignorando Id 999).
Se lo è e UnitPrice è diverso, viene aggiornato.

Guardando le domande emesse vedo che EF è alla ricerca di un record unico per nome:

SELECT TOP (2) 
[Extent1].[ProductID] AS [ProductID], 
[Extent1].[ProductName] AS [ProductName], 
[Extent1].[UnitPrice] AS [UnitPrice] 
FROM [dbo].[Products] AS [Extent1] 
WHERE N'Prod1' = [Extent1].[ProductName] 

E il prossimo (quando viene trovata una corrispondenza e UnitPrice è diverso)

update [dbo].[Products] 
set [UnitPrice] = 1.26 
where ([ProductID] = 15) 

Questo mostra che EF ha trovato un record e ora utilizza il campo chiave per eseguire l'aggiornamento.

Spero che vedere questo esempio possa far luce sulla situazione. Forse dovresti monitorare anche le istruzioni SQL e vedere se succede qualcosa di inaspettato.

+0

Gert, grazie per aver cercato di aiutare! In realtà, a City è stato assegnato 'id = j', che va da 0 a 9. Sei stato semplicemente ingannato da nomi di variabili simili. Successivamente, ho già menzionato l'effetto dell'uso di Name (nel mio caso, Slug, dato che è davvero unico) - sta lasciando il duplicato nel DB. Passare tutti gli oggetti come una matrice non ha prodotto alcun progresso. – berezovskyi

+0

Grazie per aver aggiornato la tua risposta. Non posso più verificarlo. Se qualcuno della comunità commenterà sotto la tua risposta che l'ha aiutato, segnerò la tua risposta come accettata. Mi dispiace che ci sia voluto così tanto tempo. – berezovskyi

+1

Qualche idea su come sarebbe l'espressione dell'identificatore se richiedesse due campi per identificare univocamente il record? Sto pensando p => p.ProductName, p.CategoryName (ma ovviamente non funziona). – Jarvis

1
var paidOutType = new List<PaidOutType> 
       { 
        new PaidOutType { PaidOutTypeID = 1, Code = "001", Description = "PAID OUT 1", PType = "1", Amount = 0, IsSalesSummery = true,DayFrom=1,DayTo=31 }, 
        new PaidOutType { PaidOutTypeID = 2, Code = "002", Description = "PAID OUT 2", PType = "1", Amount = 0, IsSalesSummery = true,DayFrom=1,DayTo=31 }, 
        new PaidOutType { PaidOutTypeID = 3, Code = "002", Description = "PAID OUT 3", PType = "1", Amount = 0, IsSalesSummery = true,DayFrom=1,DayTo=31 }, 
       }; 
       paidOutType.ForEach(u => smartPOSContext.PaidOutType.AddOrUpdate(u)); 
       smartPOSContext.SaveChanges(); 
Problemi correlati