web-dev-qa-db-fra.com

Saisie manuelle de clés avec Entity Framework

J'essaie d'utiliser le code Entity Framework d'abord pour un projet de base de données simple et je rencontre un problème que je ne peux tout simplement pas comprendre.

J'ai remarqué qu'EF définissait automatiquement l'ID de mes tables en augmentant de 1 à chaque fois, ignorant complètement la valeur que j'avais entrée manuellement pour ce champ. Après quelques recherches, je comprends que la bonne façon de désactiver ce comportement est de faire:

modelBuilder.Entity<Event>().Property(e => e.EventID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

Mais maintenant, je reçois juste cette erreur et je ne sais pas pourquoi:

Exception non gérée: System.Data.Entity.Infrastructure.DbUpdateException: une erreur s'est produite lors de la mise à jour des entrées. Voir l'exception interne pour plus de détails. ---

System.Data.UpdateException: une erreur s'est produite lors de la mise à jour des entrées. Voir l'exception interne pour plus de détails. ---> System.Data.SqlClient.SqlException: impossible d'insérer une valeur explicite pour la colonne d'identité dans la table 'Events' lorsque IDENTITY_INSERT est défini sur OFF.

Si c'est utile, voici la classe POCO en question:

public class Event
{
    [Key, Required]
    public int EventID { get; set; }

    public string EventType { get; set; } //TODO: Event Type Table later on
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }

    public virtual ICollection<Match> Matches { get; set; }
    public virtual ICollection<EventParticipation> EventParticipation { get; set; }
}

Merci d'avance.

45
JCafe

Par défaut, Entity Framework suppose qu'une clé primaire entière est générée par la base de données (équivalent à l'ajout de l'attribut HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity) ou à l'appel Property(e => e.EventID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); dans l'API Fluent.

Si vous regardez la migration qui crée la table, vous devriez voir ceci:

   CreateTable(
                "dbo.Events",
                c => new
                    {
                        EventID = c.Int(nullable: false, identity: true),
                        //etc
                    })
                .PrimaryKey(t => t.EventID );

Vous avez ensuite modifié le modèle à l'aide de l'API Fluent en DatabaseGenerated.None. EF met cela dans la migration:

AlterColumn("dbo.Events", "EventID", c => c.Int(nullable: false, identity: false))

Et le sql généré est le suivant:

ALTER TABLE [dbo].[Events] ALTER COLUMN [EventID] [int] NOT NULL

Ce qui fait vraiment un squat désordonné. Supprimer l'IDENTITÉ d'une colonne n'est pas trivial. Vous devez supprimer et recréer la table ou créer une nouvelle colonne, puis vous devez copier les données et corriger les clés étrangères. Il n'est donc pas surprenant qu'EF ne le fasse pas pour vous.

Vous devez déterminer la meilleure façon de le faire par vous-même. Vous pouvez restaurer vos migrations à 0 et rééchafauder à partir de zéro maintenant que vous avez spécifié DatabaseGeneratedOption.None, ou vous pouvez modifier la migration manuellement pour supprimer et recréer la table.

Ou vous pouvez supprimer et recréer la colonne:

DropColumn("Customer", "CustomerId"); 
AddColumn("Customer", "CustomerId", c => c.Long(nullable: false, identity: false));

[~ # ~] modifier [~ # ~] Ou vous pouvez Activer/désactiver l'identité avec une opération de migration personnalisée

38
Colin

Puisque je préfère les attributs, voici l'alternative par souci d'exhaustivité:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

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

Remarque: Cela fonctionne également dans EF Core.

67
marsze