J'utilise la configuration EF5 basée sur DBContext de Code-First.
Dans DbMigrationsConfiguration.Seed
j'essaie de remplir la base de données avec des données factices par défaut. Pour accomplir cette tâche, j'utilise la méthode DbSet.AddOrUpdate
.
Le code le plus simple pour illustrer mon objectif:
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++;
}
J'ai essayé d'utiliser/omettre le champ Id
ainsi que d'utiliser la propriété Id
/Slug
comme sélecteur de mise à jour.
lorsque Update-Database
est exécuté, le champ Id
est ignoré et la valeur est générée automatiquement par SQL Server et la base de données est remplie de doublons; Slug
selector autorise les doublons et produit des exceptions lors des exécutions ultérieures (Sequence contains more than one element
).
La méthode AddOrUpdate
est-elle conçue pour fonctionner de cette façon? Devrais-je effectuer l'upsert à la main?
D'abord (pas encore de réponse), AddOrUpdate
peut être appelé avec un tableau de nouveaux objets, vous pouvez donc simplement créer un tableau de type City[]
et appeler une fois context.CitySet.AddOrUpdate(cc => cc.Id, cityArray);
.
(édité)
Deuxièmement, AddOrUpdate
utilise l'identificateur expression (cc => cc.Id
) pour rechercher les villes avec le même Id
que celles du tableau. Ces villes seront mises à jour. Les autres villes du tableau seront insérées, mais leurs valeurs Id
seront générées par la base de données, car Id
est une colonne d'identité. Il ne peut pas être défini par une instruction insert. (Sauf si vous définissez Insertion identité sur). Par conséquent, lorsque vous utilisez AddOrUpdate
pour les tables avec des colonnes d'identité, vous devez trouver un autre moyen d'identifier les enregistrements, car les valeurs Id des enregistrements existants sont imprévisibles.
Dans votre cas, vous avez utilisé Slug
comme identifiant pour AddOrUpdate
, qui devrait être unique (selon votre commentaire). Je ne comprends pas pourquoi cela ne met pas à jour les enregistrements existants avec les noms correspondants Slug
s.
J'ai mis en place un petit test: ajouter ou mettre à jour une entité avec un ID (iedntity) et un nom unique:
var n = new Product { ProductID = 999, ProductName = "Prod1", UnitPrice = 1.25 };
Products.AddOrUpdate(p => p.ProductName, n);
SaveChanges();
Lorsque "Prod1" n’est pas encore là, il est inséré (en ignorant l’ID 999).
Si c'est le cas et que UnitPrice
est différent, il est mis à jour.
En regardant les requêtes émises, je constate que EF recherche un enregistrement unique par son nom:
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]
Et ensuite (lorsqu'une correspondance est trouvée et que UnitPrice
est différent)
update [dbo].[Products]
set [UnitPrice] = 1.26
where ([ProductID] = 15)
Cela montre que EF a trouvé un enregistrement et utilise maintenant le champ clé pour effectuer la mise à jour.
J'espère que voir cet exemple éclaircira votre situation. Peut-être devriez-vous également surveiller les instructions SQL et voir si quelque chose d'inattendu s'y produit.
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();