web-dev-qa-db-fra.com

Code Entity Framework Première méthode AddOrUpdate, insertion de valeurs dupliquées

J'ai une entité simple:

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

    public string Name [get; set;}
}

Ensuite, dans la méthode Seed, j'utilise AddOrUpdate pour remplir la table:

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
);

Ensuite, je lance dans la console de gestion des packages:

Add-Migration Current
Update-Database

Tout va bien: j'ai trois rangées dans le tableau "Hall". Mais si je lance à nouveau dans la console de gestion des packages Update-Database, j'ai déjà cinq lignes:

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

Pourquoi? Je pense qu'il devrait être encore trois lignes, pas cinq. J'ai essayé d'utiliser la propriété Id au lieu de Name mais cela ne fait pas la différence.

METTRE À JOUR:

Ce code produit le même résultat:

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);

De plus, j'ai la dernière entité EntityFramework installée via nuget.

53
Y.Yanavichus

Ok, je frappais mon visage du clavier pendant une heure avec ça. Si le champ Id de votre table est un champ Identity, il ne fonctionnera pas. Par conséquent, utilisez-en un autre pour identifierExpression. J'ai utilisé la propriété Name et également supprimé le champ Id de l'initialiseur new Hall {...}

Ce code Tweak to the OPs a fonctionné pour moi, alors j'espère que cela aidera quelqu'un: 

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();
}
61
Ciaran Bruen

Ce code fonctionne:

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();
}
9
Y.Yanavichus

Je sais que c’est une vieille question, mais la bonne réponse est que si vous définissez vous-même l’ID et que vous souhaitez utiliser AddOrUpdate, vous devez indiquer à EF/SQL que vous ne voulez pas qu’il génère l’identifiant.

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

L'inconvénient est que, lorsque vous insérez un nouvel élément, vous devez définir son identifiant. Par conséquent, si cette opération est effectuée de manière dynamique à l'exécution (au lieu d'utiliser des données de départ), vous devrez calculer l'identifiant suivant. Context.MyClasses.Max(c=>c.Id) + 1 fonctionne bien.

8
Anthony Nichols

Cela peut également être causé si vous définissez l'état d'entité de manière incorrecte. J'ai continué à avoir l'erreur suivante lorsque j'exécutais update-database ... "La séquence contient plus d'un élément correspondant."

Par exemple, des lignes en double étaient créées sur chaque commande update-database (ce qui, bien entendu, n’est pas censé se produire lors de l’amorçage des données), puis la commande suivante ne fonctionne pas du tout, car elle a trouvé plus d’une correspondance. (D'où l'erreur de séquence disant que j'ai plus d'une ligne correspondante). C'est parce que j'avais remplacé SaveChanges dans mon fichier de contexte par un appel de méthode à ApplyStateChanges ...

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

J'utilisais ApplyStateChanges pour m'assurer que lors de l'ajout de graphiques d'objet, Entity Framework savait explicitement si l'objet était dans un état ajouté ou modifié. L’explication complète de mon utilisation de ApplyStateChanges se trouve ici .

Et cela fonctionne très bien (mais la mise en garde !!) ... si vous amorcez également la base de données à l'aide de migrations CodeFirst, la méthode ci-dessus causera des dégâts considérables à l'appel AddOrUpdate () dans la méthode Seed. Donc avant toute chose, vérifiez simplement votre fichier DBContext et assurez-vous de ne pas surcharger SaveChanges de la manière ci-dessus, sinon vous obtiendrez des données en double en exécutant la commande update-database une seconde fois, puis ne fonctionneront pas du tout. troisième fois depuis qu'il y a plus d'une ligne pour chaque élément correspondant.

En fin de compte, vous n'avez pas besoin de configurer l'ID dans AddOrUpdate () ... qui annule tout le but de l'ensemencement simple et initial de la base de données. Cela fonctionne bien avec quelque chose comme:

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" },

aussi longtemps que je ne remplace pas la méthode SaveChanges dans mon fichier de contexte par un appel à ApplyStateChanges. J'espère que cela t'aides.

3
firecape

Cela a fonctionné pour moi

  1. Supprimer toutes les lignes de la table.
  2. Réinitialisez l'identité incrémentielle à 0. DBCC CHECKIDENT (yourtablename, RESEED, 0) (Les clés primaires spécifiées dans la fonction Seed() doivent correspondre à celles de la table de base de données afin qu'elles ne soient pas dupliquées.)
  3. Spécifiez les clés primaires dans la méthode 'seed'.
  4. Exécutez la méthode Seed() plusieurs fois et vérifiez si elles sont dupliquées.

J'ai trouvé que AddOrUpdate fonctionne bien avec les champs qui ne sont pas des ID. Si cela fonctionne pour vous: context.Halls.AddOrUpdate(h => h.Name, hall1, hall2, hall3)

Vous voudrez peut-être utiliser des noms de salle tels que 'French_test_abc_100', 'German_test_abc_100' etc.

Cela empêche les données de test codées en dur de gâcher les choses lorsque vous testez votre application.

1
Daryn

J'ai utilisé le champ ID comme identité/clé et ajouter des attributs pour ne pas attribuer d'identifiants par le serveur Cela a résolu le problème pour moi.

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

    public string Name [get; set;}
 }
0
Guntars

J'ai découvert que pour que cela fonctionne, la position d'identité devrait être 0 lors de la première utilisation de la graine. Vous pouvez le réinitialiser en utilisant:

DBCC CHECKIDENT (tableName, RESEED, 0)
0
Brain Balaka

Je pense qu'il est probable que vous deviez sauvegarder les migrations de bases de données existantes (par exemple, démarrer votre base de données à partir de zéro) avec quelque chose comme "Update-Database TargetMigration: 0" suivi de "Update-Database".

Dans l’état actuel, vous ne supprimez pas la table ou les valeurs existantes, vous ne faites qu’ajouter/mettre à jour ces valeurs. Cela doit se produire pour obtenir le résultat souhaité.

Voici une bonne référence pour les migrations EF: http://elegantcode.com/2012/04/12/entity-framework-migrations-tips/

0
Harvey Powers

Votre champ d'identification est-il un champ d'identité? Je courais dans le même problème. Lorsque j'ai supprimé le statut d'identité de mon champ ID et défini les identifiants de la base de données, le problème a été résolu.

Cela a fonctionné pour moi, étant donné qu'il s'agissait de tables de recherche et que, de toute façon, ils n'auraient pas dû être des champs d'identité.

0
Jason

Juste pour répondre à la question de Ciaran, le code ci-dessous de réinitialisation du contexte sur ModelCreating m'a aidé à résoudre des problèmes similaires. Assurez-vous de remplacer "ApplicationContext" par votre nom 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);
        }
     }

0
user1570636

Si l'id de l'objet (hall) est 0, c'est une insertion. Je pense que vous devez vérifier le champ id de vos objets hall

0
weeyoung