SqlException:Der explizite Wert für die Identitätsspalte kann nicht eingefügt werden

SqlException:Der explizite Wert für die Identitätsspalte kann nicht eingefügt werden

Wenn Sie eine Tabelle mit einer Identitätsspalte haben und versuchen, beim Einfügen eines Datensatzes den Wert für die Identitätsspalte anzugeben, erhalten Sie die folgende Ausnahme:

Dieser Fehler bedeutet, dass Sie eine Identitätsspalte in der Tabelle haben und versuchen, einen Wert dafür festzulegen. Wenn Sie eine Identitätsspalte wie diese haben, wird ihr Wert automatisch generiert, wenn Sie sie einfügen, weshalb Sie daran gehindert werden, einen Wert für diese Spalte zu übergeben.

Nehmen wir zum Beispiel an, Ihre Tabelle hat die folgende Definition:

CREATE TABLE [dbo].[Movies](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[Name] [nvarchar](500) NOT NULL,
	[YearOfRelease] [int] NOT NULL,
	[Description] [nvarchar](500) NOT NULL,
	[Director] [nvarchar](100) NOT NULL,
	[BoxOfficeRevenue] [decimal](18, 2) NOT NULL,
 CONSTRAINT [PK_Movies] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Code language: SQL (Structured Query Language) (sql)

Ich werde einige verschiedene Lösungen zur Behebung dieses Problems zeigen.

Hinweis:Die folgenden Lösungen zeigen Codebeispiele mit EF Core. Wenn Sie ADO.NET oder ein anderes ORM (wie Dapper) verwenden, würden die gleichen Lösungen auch funktionieren (nur mit anderem Code).

Option 1 – Geben Sie die Identitätsspalte beim Einfügen nicht an

Die erste Option ist die einfachste – versuchen Sie nicht, den Wert für die Identitätsspalte festzulegen:

using (var context = new StreamingServiceContext(connectionString))
{
	context.Movies.Add(new Movie()
	{
		//Id = 20,
		Name = "Godzilla",
		Description = "Nuclear lizard fights monsters",
		Director = "Gareth Edwards",
		YearOfRelease = 2014,
		BoxOfficeRevenue = 529_000_000.00m
	});

	context.SaveChanges();
}
Code language: C# (cs)

Wenn Sie den Datensatz einfügen, generiert SQL Server den Wert für Sie und EF Core aktualisiert die Eigenschaft mit dem automatisch generierten Wert.

Option 2 – Aktivieren Sie IDENTITY_INSERT

In einigen Fällen möchten Sie die ID möglicherweise explizit festlegen, anstatt sie automatisch für Sie generieren zu lassen. In diesem Fall müssten Sie IDENTITY_INSERT wie folgt aktivieren:

using (var context = new StreamingServiceContext(connectionString))
{
	using (var transaction = context.Database.BeginTransaction())
	{
		context.Movies.Add(new Movie()
		{
			Id = 20,
			Name = "Godzilla",
			Description = "Nuclear lizard fights monsters",
			Director = "Gareth Edwards",
			YearOfRelease = 2014,
			BoxOfficeRevenue = 529_000_000.00m
		});

		context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT dbo.Movies ON;");
		context.SaveChanges();
		context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT dbo.Movies OFF;");
		transaction.Commit();
	}
}
Code language: C# (cs)

Hinweis:Wenn Sie EF Core verwenden, müssen Sie die Abfrage innerhalb einer Transaktion ausführen, damit dies funktioniert.

IDENTITY_INSERT kann nur für jeweils eine Tabelle pro Sitzung aktiviert sein.

Angenommen, Sie versuchen, IDENTITY_INSERT für zwei Tabellen gleichzeitig zu aktivieren:

context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT dbo.Movies ON;");
context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT dbo.Actors ON;");
Code language: C# (cs)

Sie erhalten die folgende Ausnahme:

Diese Einschränkung gilt nur pro Sitzung. Wenn eine andere Sitzung IDENTITY_INSERT für die Schauspielertabelle in ihrer Sitzung aktiviert, können Sie IDENTITY_INSERT für Filme in einer anderen Sitzung zur gleichen Zeit aktivieren.

Option 3 – Entfernen Sie die IDENTITY-Spezifikation aus der Spalte

Wenn Sie sich in einer Entwicklungsumgebung befinden und nicht bemerkt haben, dass Sie eine Identitätsspalte haben, bis Sie auf diese Ausnahme beim Einfügen von Identitäten gestoßen sind, möchten Sie wahrscheinlich einfach die IDENTITY-Spezifikation aus der Spalte entfernen.

Wenn Sie EF Core zum Erstellen Ihrer Tabellen verwenden, geben Sie mit dem DatabaseGenerated(DatabaseGeneratedOption.None))-Attribut an, dass die Spalte keine Identitätsspalte sein soll.

using System.ComponentModel.DataAnnotations.Schema;

public class Movie
{
	[Key]
	[DatabaseGenerated(DatabaseGeneratedOption.None)]
	public int Id { get; set; }
	
	//rest of class
}
Code language: C# (cs)

EF Core verarbeitet diese Schemaänderung nicht korrekt. Anstatt dies als Schemaänderung zu versuchen, wiederholen Sie die Migration, die die Tabelle erstellt hat.

Angenommen, Sie haben zwei Migrationen – Database_v1_Init und Database_v2_CreateMoviesTable – und Sie möchten die Movies-Tabelle so ändern, dass sie keine Identitätsspalte enthält. Führen Sie die folgenden Schritte aus, um die Migration von Database_v2_CreateMoviesTable zu wiederholen:

  • Migrieren Sie nach unten zu Database_v1_Init:
dotnet ef database update Database_v1_Init
Code language: PowerShell (powershell)
  • Entfernen Sie die letzte Migration, in diesem Fall Database_v2_CreateMoviesTable:
dotnet ef migrations remove
Code language: PowerShell (powershell)

Hinweis:Löschen Sie nicht einfach die Migrationsdatei, da die Modell-Snapshot-Datei sonst nicht mehr synchron ist.

  • Fügen Sie das Attribut [DatabaseGenerated(DatabaseGeneratedOption.None)] zur Eigenschaft Movie.Id hinzu.
public class Movie
{
	[Key]
	[DatabaseGenerated(DatabaseGeneratedOption.None)]
	public int Id { get; set; }
Code language: C# (cs)
  • Migration Database_v2_CreateMoviesTable neu erstellen:
dotnet ef migrations add Database_v2_CreateMoviesTable
Code language: PowerShell (powershell)
  • Sehen Sie sich den generierten Migrationsquellcode in _Database_v2_CreateMoviesTable.cs. an Zunächst können Sie sehen, dass die Spalte nicht mit einer IDENTITY-Spezifikation erstellt wird. Zweitens sollte diese Migration nur die Movies-Tabelle erstellen. Wenn es etwas anderes tut, ist die Snapshot-Datei des Modells wahrscheinlich in einen ungültigen Zustand geraten (wahrscheinlich aufgrund des manuellen Löschens der Migrationsdateien).
public partial class Database_v2_CreateMoviesTable : Migration
{
	protected override void Up(MigrationBuilder migrationBuilder)
	{
		migrationBuilder.CreateTable(
			name: "Movies",
			columns: table => new
			{
				Id = table.Column<int>(type: "int", nullable: false),
				Name = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: false),
				YearOfRelease = table.Column<int>(type: "int", nullable: false),
				Description = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: false),
				Director = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
				BoxOfficeRevenue = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
			},
			constraints: table =>
			{
				table.PrimaryKey("PK_Movies", x => x.Id);
			});
			
			//rest of class not shown
}
Code language: C# (cs)
  • Wenden Sie die Migration an:
dotnet ef database update Database_v2_CreateMoviesTable
Code language: PowerShell (powershell)

Jetzt, da diese ID-Spalte nicht mehr die IDENTITY-Spezifikation hat, können Sie Datensätze in die Tabelle einfügen, während Sie einen Wert für die ID angeben.

Haftungsausschluss:Dies gilt nicht für Produktionsumgebungen. Sie verlieren Daten, wenn Sie die Tabelle löschen und neu erstellen. Ich würde diesen Ansatz nur empfehlen, wenn Sie sich in einer Entwicklungsumgebung befinden und mit dem Verlust von Daten einverstanden sind.

Option 4 – Bei einer Aktualisierung zuerst den Datensatz abrufen

Um eine Aktualisierung anstelle einer Einfügung vorzunehmen, müssen Sie zuerst den Datensatz abrufen. Andernfalls generiert EF Core beim Aufrufen von SaveChanges() eine Einfügeanweisung. Wenn Sie versucht haben, den Wert für die Identitätsspalte anzugeben, stoßen Sie auf die Ausnahme beim Einfügen von Identitäten.

So aktualisieren Sie einen Datensatz, indem Sie ihn zuerst abrufen:

using (var context = new StreamingServiceContext(connectionString))
{
	var movie = await context.Movies.FirstOrDefaultAsync(t => t.Id == 20);
	movie.Description = "Nuclear lizard fights monsters";
	
	context.SaveChanges();
}
Code language: C# (cs)