web-dev-qa-db-fra.com

Entity Framework 6 GUID en tant que clé primaire: impossible d'insérer la valeur NULL dans la colonne 'Id', table 'FileStore'; la colonne n'autorise pas les valeurs NULL

J'ai une entité avec la clé primaire "Id" qui est Guid:

public class FileStore
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Path { get; set; }
}

Et une configuration:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<FileStore>().Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    base.OnModelCreating(modelBuilder);
}

Lorsque j'essaie d'insérer un enregistrement, une erreur se produit:

Impossible d'insérer la valeur NULL dans la colonne 'Id', table 'FileStore'; La colonne n'autorise pas les valeurs NULL. INSERT échoue.\R\nL'instruction a été terminée.

Je ne veux pas générer Guid manuellement. Je veux juste insérer un enregistrement et obtenir Id généré par SQL Server. Si je définis .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity), Id la colonne n'est pas une colonne d'identité dans SQL Server.

Comment configurer Entity Framework pour générer automatiquement Guid dans SQL Server?

70
Algirdas

En plus d'ajouter ces attributs à votre colonne Id:

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }

dans votre migration, vous devriez changer votre CreateTable pour ajouter la propriété defaultValueSQL à votre colonne, c.-à-d.:

Id = c.Guid(nullable: false, identity: true, defaultValueSql: "newsequentialid()"),

Cela vous évitera d'avoir à toucher manuellement votre base de données, ce que, comme vous l'avez souligné dans les commentaires, vous voulez éviter avec Code First.

91
lightyeare

essaye ça :

public class FileStore
 {
   [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
   public Guid Id { get; set; }
   public string Name { get; set; }
   public string Path { get; set; }
 }

Vous pouvez vérifier ceci SO post .

16
user3383479

Vous pouvez définir la valeur par défaut de votre ID dans votre base de données sur newsequentialid () ou newid (). Ensuite, la configuration d'identité de EF devrait fonctionner.

8
Murdock

Cela m'est arrivé avant.

Lorsque la table a été créée et que j'ai ajouté dans .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity) plus tard, la migration du code n'a pas pu affecter de valeur par défaut à la colonne Guid.

Le correctif:

Tout ce dont nous avons besoin est d'aller dans la base de données, sélectionnez la colonne Id et ajoutez newsequentialid() manuellement dans Default Value or Binding.

Pas besoin de mettre à jour la table dbo .__ MigrationHistory.

J'espère que ça aide.


La solution consistant à ajouter New Guid() n'est généralement pas préférable, car en théorie , il existe une possibilité que vous obteniez un duplicata accidentellement.


Et vous ne devriez pas vous inquiéter de l'édition directe dans la base de données. Tout Entity Framework automatise une partie de notre travail de base de données.

Traduction en cours

.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)

dans

[Id] [uniqueidentifier] NOT NULL DEFAULT newsequentialid(),

Si d'une manière ou d'une autre notre EF a omis une chose et n'a pas ajouté la valeur par défaut pour nous, continuez et ajoutez-le manuellement.

5
Blaise

Cela fonctionne pour moi (pas Azure), SQL 2008 R2 sur un serveur dev ou localdb\mssqllocaldb sur un poste de travail local. Remarque: l'entité ajoute les colonnes Create, CreateBy, Modified, ModifiedBy et Version.

public class Carrier : Entity
{
    public Guid Id { get; set; }
    public string Code { get; set; }
    public string Name { get; set; }
}

puis créez une classe de configuration de mappage

public class CarrierMap : EntityTypeConfiguration<Carrier>
{
    public CarrierMap()
    {
        HasKey(p => p.Id);

        Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        Property(p => p.Code)
            .HasMaxLength(4)
            .IsRequired()
            .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute { IsClustered = true, IsUnique = true }));

        Property(p => p.Name).HasMaxLength(255).IsRequired();
        Property(p => p.Created).HasPrecision(7).IsRequired();
        Property(p => p.Modified)
            .HasColumnAnnotation("IX_Modified", new IndexAnnotation(new IndexAttribute()))
            .HasPrecision(7)
            .IsRequired();
        Property(p => p.CreatedBy).HasMaxLength(50).IsRequired();
        Property(p => p.ModifiedBy).HasMaxLength(50).IsRequired();
        Property(p => p.Version).IsRowVersion();
    }
}

Cela crée une méthode Up dans la migration DbMigrale initiale lorsque vous exécutez add-migration comme ceci

        CreateTable(
            "scoFreightRate.Carrier",
            c => new
                {
                    Id = c.Guid(nullable: false, identity: true),
                    Code = c.String(nullable: false, maxLength: 4),
                    Name = c.String(nullable: false, maxLength: 255),
                    Created = c.DateTimeOffset(nullable: false, precision: 7),
                    CreatedBy = c.String(nullable: false, maxLength: 50),
                    Modified = c.DateTimeOffset(nullable: false, precision: 7,
                        annotations: new Dictionary<string, AnnotationValues>
                        {
                            { 
                                "IX_Modified",
                                new AnnotationValues(oldValue: null, newValue: "IndexAnnotation: { }")
                            },
                        }),
                    ModifiedBy = c.String(nullable: false, maxLength: 50),
                    Version = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
                })
            .PrimaryKey(t => t.Id)
            .Index(t => t.Code, unique: true, clustered: true);

Remarque: que les colonnes Id ne reçoivent pas de valeur par défaut, ne vous inquiétez pas

Maintenant, exécutez Update-Database, et vous devriez vous retrouver avec une définition de table dans votre base de données comme ceci:

CREATE TABLE [scoFreightRate].[Carrier] (
    [Id]         UNIQUEIDENTIFIER   DEFAULT (newsequentialid()) NOT NULL,
    [Code]       NVARCHAR (4)       NOT NULL,
    [Name]       NVARCHAR (255)     NOT NULL,
    [Created]    DATETIMEOFFSET (7) NOT NULL,
    [CreatedBy]  NVARCHAR (50)      NOT NULL,
    [Modified]   DATETIMEOFFSET (7) NOT NULL,
    [ModifiedBy] NVARCHAR (50)      NOT NULL,
    [Version]    ROWVERSION         NOT NULL,
    CONSTRAINT [PK_scoFreightRate.Carrier] PRIMARY KEY NONCLUSTERED ([Id] ASC)
);


GO
CREATE UNIQUE CLUSTERED INDEX [IX_Code]
    ON [scoFreightRate].[Carrier]([Code] ASC);

Remarque: nous avons substitué SqlServerMigrationSqlGenerator pour nous assurer qu'il ne transforme PAS la clé primaire en index clusterisé, car nous encourageons nos développeurs à définir un meilleur index clusterisé sur les tables

public class OurMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
    protected override void Generate(AddPrimaryKeyOperation addPrimaryKeyOperation)
    {
        if (addPrimaryKeyOperation == null) throw new ArgumentNullException("addPrimaryKeyOperation");
        if (!addPrimaryKeyOperation.Table.Contains("__MigrationHistory"))
            addPrimaryKeyOperation.IsClustered = false;
        base.Generate(addPrimaryKeyOperation);
    }

    protected override void Generate(CreateTableOperation createTableOperation)
    {
        if (createTableOperation == null) throw new ArgumentNullException("createTableOperation");
        if (!createTableOperation.Name.Contains("__MigrationHistory"))
            createTableOperation.PrimaryKey.IsClustered = false;
        base.Generate(createTableOperation);
    }

    protected override void Generate(MoveTableOperation moveTableOperation)
    {
        if (moveTableOperation == null) throw new ArgumentNullException("moveTableOperation");
        if (!moveTableOperation.CreateTableOperation.Name.Contains("__MigrationHistory")) moveTableOperation.CreateTableOperation.PrimaryKey.IsClustered = false;
        base.Generate(moveTableOperation);
    }
}
5
Allan

Entity Framework - Utilisez un GUID comme clé primaire

L'utilisation d'une Guid comme clé primaire de vos tables, lorsque vous utilisez Entity Framework, nécessite un peu plus d'effort que si vous utilisiez un entier. Le processus d’installation est simple, une fois que vous avez lu/été montré comment le faire.

Le processus est légèrement différent pour les approches Code First et Database First. Cet article traite des deux techniques.

entrez la description de l'image ici

Code d'abord

Utiliser un Guid comme clé primaire lors de la première approche du code est simple. Lors de la création de votre entité, ajoutez l'attribut DatabaseGenerated à votre propriété de clé primaire, comme indiqué ci-dessous.

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }

Entity Framework créera la colonne comme vous le souhaitez, avec une clé primaire et un type de données uniqueidentifier.

codefirst-defaultvalue

Notez également, très important, que la valeur par défaut de la colonne a été définie sur (newsequentialid()). Cela génère un nouveau Guid séquentiel (continu) pour chaque ligne. Si cela vous tenait à coeur, vous pourriez changer ceci en newid()), ce qui donnerait un Guid totalement aléatoire pour chaque nouvelle ligne. Cela sera effacé chaque fois que votre base de données sera supprimée et recréée. Cela fonctionnera donc mieux avec l'approche Base de données d'abord.

Base de données en premier

La première approche de la base de données suit une ligne similaire à celle du premier code, mais vous devrez modifier manuellement votre modèle pour le faire fonctionner.

Veillez à modifier la colonne de clé primaire et à ajouter la fonction (newsequentialid ()) ou (newid ()) en tant que valeur par défaut avant toute action.

entrez la description de l'image ici

Ensuite, ouvrez votre diagramme EDMX, sélectionnez la propriété appropriée et ouvrez la fenêtre des propriétés. Assurez-vous que StoreGeneratedPattern est défini sur identité.

databasefirst-model

Inutile de donner à votre entité un identifiant dans votre code, qui sera automatiquement renseigné après validation de l'entité dans la base de données;

using (ApplicationDbContext context = new ApplicationDbContext())
{
    var person = new Person
                     {
                         FirstName = "Random",
                         LastName = "Person";
                     };

    context.People.Add(person);
    context.SaveChanges();
    Console.WriteLine(person.Id);
}

Remarque importante: votre champ Guid DOIT être une clé primaire, sinon cela ne fonctionne pas. Entity Framework vous donnera un message d'erreur plutôt cryptique!

Résumé

Guid (identificateurs globaux uniques) peut facilement être utilisé comme clé primaire dans Entity Framework. Un petit effort supplémentaire est nécessaire pour le faire, selon l’approche choisie. Lorsque vous utilisez la première approche de code, ajoutez l'attribut DatabaseGenerated à votre champ de clé. Lorsque vous utilisez l'approche Base de données d'abord, définissez explicitement StoredGeneratedPattern sur Identity sur votre modèle.

[1]: https://i.stack.imgur.com/IxGdd.png
[2]: https://i.stack.imgur.com/Qssea.png
4
Vinu Das

Selon this , DatabaseGeneratedOption.Identity n'est pas détecté par une migration spécifique s'il est ajouté après la table a été créée. C'est le cas que je rencontre. J'ai donc abandonné la base de données et cette migration spécifique et ajouté une nouvelle migration. Enfin, mettez à jour la base de données, puis tout fonctionne comme prévu. J'utilise EF 6.1, SQL2014 et VS2013.

4
stt106

Si vous utilisez Code-First et avez déjà une base de données:

public override void Up()
{
    AlterColumn("dbo.MyTable","Id", c =>  c.Guid(nullable: false, identity: true, defaultValueSql: "newsequentialid()"));
}
2
Someone OnEarth

Vous ne pouvez pas. Vous allez/allez casser beaucoup de choses. Comme les relations. Ce qui compte sur le nombre qui est retiré, ce que EF ne peut pas faire de la manière que vous le configurez. Le prix pour briser tous les modèles existants.

Générez le GUID dans la couche C # afin que les relations puissent continuer de fonctionner.

1
TomTom

Et qu'est-ce qui ressemble à ça?

public class Carrier : Entity
{
    public Carrier()
    {
         this.Id = Guid.NewGuid();
    }  
    public Guid Id { get; set; }
    public string Code { get; set; }
    public string Name { get; set; }
}
0
Miroslav Siska