web-dev-qa-db-de.com

Entity Framework 6 GUID als Primärschlüssel: Der Wert NULL kann nicht in Spalte 'Id', Tabelle 'FileStore' eingefügt werden, Spalte erlaubt keine Nullen

Ich habe eine Entität mit dem Primärschlüssel "Id", die Guid ist:

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

Und etwas Konfiguration:

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

Wenn ich versuche, einen Datensatz einzufügen, wird folgende Fehlermeldung angezeigt:

Der Wert NULL kann nicht in die Spalte 'Id' in der Tabelle 'FileStore' eingefügt werden. Spalte erlaubt keine Nullen. INSERT schlägt fehl.\R\nDie Anweisung wurde abgebrochen.

Ich möchte Guid nicht manuell generieren. Ich möchte nur einen Datensatz einfügen und Id von SQL Server generieren lassen. Wenn ich .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity) einstelle, ist die Id -Spalte keine Identitätsspalte in SQL Server.

Wie kann ich Entity Framework so konfigurieren, dass Guid in SQL Server automatisch generiert wird?

70
Algirdas

Zusätzlich zum Hinzufügen dieser Attribute zu Ihrer ID-Spalte:

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

bei Ihrer Migration sollten Sie Ihr CreateTable ändern, um die defaultValueSQL -Eigenschaft zu Ihrer Spalte hinzuzufügen, d. h .:

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

Auf diese Weise müssen Sie Ihre Datenbank nicht manuell berühren, was Sie, wie Sie in den Kommentaren ausgeführt haben, mit Code First vermeiden möchten.

91
lightyeare

versuche dies :

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

Sie können dies überprüfen SO post .

16
user3383479

Sie können den Standardwert Ihrer ID in Ihrer Datenbank auf newsequentialid () oder newid () setzen. Dann sollte die Identitätskonfiguration von EF funktionieren.

8
Murdock

Das ist mir schon mal passiert.

Nachdem die Tabelle erstellt und später in .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity) hinzugefügt wurde, konnte die Codemigration der Guid-Spalte irgendwie keinen Standardwert zuweisen.

Die Reparatur:

Alles, was wir brauchen, ist, in die Datenbank zu gehen, die ID-Spalte auszuwählen und newsequentialid() manuell in Default Value or Binding Einzufügen.

Es ist nicht erforderlich, die Tabelle dbo .__ MigrationHistory zu aktualisieren.

Ich hoffe es hilft.


Die Lösung des Hinzufügens von New Guid() wird im Allgemeinen nicht bevorzugt, da theoretisch is die Möglichkeit besteht, dass Sie versehentlich ein Duplikat erhalten.


Und Sie sollten sich keine Gedanken über die direkte Bearbeitung in der Datenbank machen. Entity Framework automatisiert lediglich einen Teil unserer Datenbankarbeit.

Übersetzen

.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)

in

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

Wenn unser EF auf irgendeine Weise etwas übersehen hat und den Standardwert für uns nicht hinzugefügt hat, fügen Sie ihn einfach manuell hinzu.

5
Blaise

Dies funktioniert für mich (kein Azure), SQL 2008 R2 auf dem Entwicklungsserver oder localdb\mssqllocaldb auf der lokalen Arbeitsstation. Hinweis: Die Entität fügt die Spalten "Create", "CreateBy", "Modified", "ModifiedBy" und "Version" hinzu.

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

erstellen Sie dann eine Zuordnungskonfigurationsklasse

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

Dadurch wird in der ersten DbMigration eine Up-Methode erstellt, wenn Sie die folgende Add-Migration ausführen

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

Hinweis: Die ID-Spalten erhalten keinen Standardwert, keine Sorge

Führen Sie nun Update-Database aus und Sie sollten eine Tabellendefinition in Ihrer Datenbank wie folgt erhalten:

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

Hinweis: Wir haben den SqlServerMigrationSqlGenerator überschrieben, um sicherzustellen, dass der Primärschlüssel NICHT zu einem Clustered-Index wird, da wir unsere Entwickler ermutigen, einen besseren Clustered-Index für Tabellen festzulegen.

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 - Verwenden Sie eine Guid als Primärschlüssel

Die Verwendung einer Guid als Tabellenprimärschlüssel erfordert bei Verwendung von Entity Framework etwas mehr Aufwand als bei Verwendung einer Ganzzahl. Der Einrichtungsvorgang ist unkompliziert, nachdem Sie die Anleitung gelesen/gelesen haben.

Der Prozess unterscheidet sich geringfügig für die Ansätze Code First und Database First. In diesem Beitrag werden beide Techniken erläutert.

Bildbeschreibung hier eingeben

Code zuerst

Die Verwendung einer Guid als Primärschlüssel bei der ersten Code-Annäherung ist einfach. Fügen Sie beim Erstellen Ihrer Entität das DatabaseGenerated-Attribut zu Ihrer Primärschlüsseleigenschaft hinzu, wie unten gezeigt.

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

Das Entitäts-Framework erstellt die Spalte erwartungsgemäß mit einem Primärschlüssel und einem Datentyp mit eindeutiger Kennung.

codefirst-defaultvalue

Beachten Sie auch, dass der Standardwert in der Spalte auf (newsequentialid()) Gesetzt wurde. Dadurch wird für jede Zeile eine neue fortlaufende Guid erstellt. Wenn Sie so geneigt wären, könnten Sie dies in newid()) ändern, was zu einer völlig zufälligen Guid für jede neue Zeile führen würde. Dies wird jedes Mal gelöscht, wenn Ihre Datenbank gelöscht und neu erstellt wird. Dies funktioniert also besser, wenn Sie den Ansatz "Datenbank zuerst" wählen.

Datenbank zuerst

Der erste Datenbankansatz folgt einem ähnlichen Ansatz wie der erste Codeansatz, aber Sie müssen Ihr Modell manuell bearbeiten, damit es funktioniert.

Stellen Sie sicher, dass Sie die Primärschlüsselspalte bearbeiten und die Funktion (newsequentialid ()) oder (newid ()) als Standardwert hinzufügen, bevor Sie etwas tun.

Bildbeschreibung hier eingeben

Öffnen Sie als Nächstes Ihr EDMX-Diagramm, wählen Sie die entsprechende Eigenschaft aus und öffnen Sie das Eigenschaftenfenster. Stellen Sie sicher, dass StoreGeneratedPattern auf identity festgelegt ist.

databasefirst-model

Es ist nicht erforderlich, Ihrer Entität eine ID in Ihrem Code zuzuweisen, die automatisch ausgefüllt wird, nachdem die Entität in die Datenbank übernommen wurde.

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

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

Wichtiger Hinweis: Ihr Guid-Feld MUSS ein Primärschlüssel sein, da dies sonst nicht funktioniert. Entity Framework gibt Ihnen eine eher kryptische Fehlermeldung!

Zusammenfassung

Guid (Globally Unique Identifiers) kann problemlos als Primärschlüssel in Entity Framework verwendet werden. Dazu ist ein wenig zusätzlicher Aufwand erforderlich, je nachdem, welchen Ansatz Sie wählen. Fügen Sie Ihrem Schlüsselfeld das DatabaseGenerated-Attribut hinzu, wenn Sie den Code First Approach verwenden. Setzen Sie bei der Methode "Database First" das StoredGeneratedPattern in Ihrem Modell explizit auf "Identity".

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

Laut this wird DatabaseGeneratedOption.Identity von einer bestimmten Migration nicht erkannt, wenn es hinzugefügt wird after die Tabelle wurde erstellt, was der Fall ist, in dem ich ausgeführt werde. Also habe ich die Datenbank und diese spezifische Migration gelöscht und eine neue Migration hinzugefügt, schließlich die Datenbank aktualisiert, dann funktioniert alles wie erwartet. Ich benutze EF 6.1, SQL2014 und VS2013.

4
stt106

Wenn Sie Code-First ausführen und bereits eine Datenbank haben:

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

Du kannst nicht. Sie werden eine Menge Dinge zerbrechen. Wie Beziehungen. Die sich darauf verlassen, dass die Nummer zurückgezogen wird, was EF so nicht kann, wie Sie es eingerichtet haben. Der Preis für das Brechen jedes Musters, das es gibt.

Generieren Sie die GUID in der C # -Ebene, damit die Beziehungen weiter funktionieren können.

1
TomTom

Und was ist so etwas?

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