Start Debugging

Fix: The seed entity for entity type 'X' cannot be added because a non-zero value is required for property 'Id'

HasData seeds an entity with a store-generated key but no explicit value. Give every seed row a stable non-zero Id, or switch to UseSeeding for generated keys.

The fix: you called HasData to seed an entity whose primary key is store-generated (an IDENTITY column), but you left the key at its CLR default (0 for int, Guid.Empty for Guid). HasData builds its insert script at migration time without touching the database, so it cannot rely on the database to hand out keys. Give every seeded row an explicit, stable, non-zero key value (new Country { Id = 1, ... }). If you actually want the database to generate the key, do not use HasData at all: use UseSeeding/UseAsyncSeeding instead. This guide is written against .NET 11, C# 14, and Microsoft.EntityFrameworkCore 11.0.0, but the message text and behaviour are unchanged back to EF Core 2.1.

System.InvalidOperationException: The seed entity for entity type 'Country' cannot be added
because a non-zero value is required for property 'Id'. Consider providing a negative value
to avoid collisions with non-seed data.
   at Microsoft.EntityFrameworkCore.Metadata.Internal.EntityType.<>c__DisplayClass...
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.ValidateData(IModel model, ...)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.Validate(IModel model, ...)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure.ModelRuntimeInitializer.Initialize(...)

This is a model-validation error, not a runtime query error. It fires the first time EF Core builds your model: the first query, the first SaveChanges, the first context.Model access, or, most commonly, when you run dotnet ef migrations add. The type name in quotes is the entity you seeded; the property in quotes is its primary key. The “consider providing a negative value” hint at the end is real advice, not boilerplate, and the section below on collisions explains why.

Why HasData needs the key spelled out

HasData (the docs now call it model managed data, since “seeding” oversold what it does) is owned by migrations. When you add a migration, EF Core diffs the seed rows in your current model against the rows it recorded in the last model snapshot, and emits InsertData, UpdateData, or DeleteData calls to reconcile them. That diff happens at design time, on your machine, with no database connection.

The primary key is what makes the diff possible. EF Core uses it to recognise “this is the same row I inserted last migration, but the Name changed” versus “this is a brand new row”. Without a stable key value, it has nothing to match on across migrations. So the model validator enforces a hard rule: every HasData row must carry an explicit value for its primary key, even when that key is configured as store-generated.

When the key is store-generated and you leave it at the CLR default, EF Core cannot tell the difference between “the developer forgot to set the key” and “the developer wants key 0”. Rather than silently insert a row with Id = 0 (which collides badly with identity columns), it throws. The official limitations of model managed data list this first: “The primary key value needs to be specified even if it’s usually generated by the database. It will be used to detect data changes between migrations.”

The smallest repro

A single entity with a conventional Id and one HasData call that omits it.

// .NET 11, C# 14, Microsoft.EntityFrameworkCore 11.0.0
public class Country
{
    public int Id { get; set; }      // conventional PK -> store-generated IDENTITY
    public string Name { get; set; } = "";
}

public class AppDbContext : DbContext
{
    public DbSet<Country> Countries => Set<Country>();

    protected override void OnConfiguring(DbContextOptionsBuilder options)
        => options.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=SeedRepro;Trusted_Connection=True");

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Country>().HasData(
            new Country { Name = "USA" },      // no Id -> Id stays 0 -> throws
            new Country { Name = "Canada" });
    }
}

Run dotnet ef migrations add Seed against this and you get the exception. The Id properties are 0, EF Core treats 0 as “no value” for a store-generated int key, and validation fails before any migration code is written.

Fix 1: give every seed row an explicit, stable key

This is the correct fix for genuine reference data: short, fixed lookup tables that change only through migrations (countries, currencies, roles, statuses). Assign each row a key value by hand and never reuse or renumber it.

// .NET 11, C# 14, EF Core 11.0.0
modelBuilder.Entity<Country>().HasData(
    new Country { Id = 1, Name = "USA" },
    new Country { Id = 2, Name = "Canada" },
    new Country { Id = 3, Name = "Mexico" });

Two rules keep this working over time:

  1. The key values are now part of your schema history. Treat them as immutable. If you change row 2 from Id = 2 to Id = 22, the next migration emits a DeleteData for 2 and an InsertData for 22, which throws away the old row and anything that referenced it by foreign key.
  2. Pick the values explicitly, do not let them drift. Hard-coded constants are fine. What you must not do is compute them from anything non-deterministic (a loop counter that depends on collection order, DateTime.Now, a Guid.NewGuid() call), because the migration diff must produce the same values on every machine.

If the key is a Guid, the same rule applies: supply a fixed, hard-coded Guid, not Guid.NewGuid(). A freshly generated GUID on every build makes every migration think the row changed.

// .NET 11, C# 14, EF Core 11.0.0 - fixed GUIDs, not Guid.NewGuid()
modelBuilder.Entity<Role>().HasData(
    new Role { Id = new Guid("3f2504e0-4f89-41d3-9a0c-0305e82c3301"), Name = "Admin" },
    new Role { Id = new Guid("3f2504e0-4f89-41d3-9a0c-0305e82c3302"), Name = "User" });

Fix 2: use negative keys to dodge identity collisions

The exception’s own hint, “consider providing a negative value to avoid collisions with non-seed data”, solves a problem you hit later, not the one in front of you. When you seed Id = 1, 2, 3 into a SQL Server IDENTITY column, the identity counter does not know about your seeded rows. The first real INSERT after seeding starts the counter at 1, tries to use a key your seed already took, and fails with a primary-key violation, or IDENTITY_INSERT machinery gets involved and things get messy.

Negative keys for seed data sidestep this. Real, user-generated rows count up from 1; your seeded reference rows live below 0 and never overlap.

// .NET 11, C# 14, EF Core 11.0.0
modelBuilder.Entity<Country>().HasData(
    new Country { Id = -1, Name = "USA" },
    new Country { Id = -2, Name = "Canada" },
    new Country { Id = -3, Name = "Mexico" });

This is the long-standing recommendation from the EF Core team for IDENTITY-backed lookup tables that also receive runtime inserts. It is overkill for a table that is only ever seeded (a pure lookup table the app never inserts into), where positive keys read more naturally.

Fix 3: stop using HasData for this data

If your reason for reaching for HasData was “I just want some initial rows in the database”, you probably do not want model managed data at all. HasData is built for static, deterministic, migration-owned data and nothing else. For data that should use database-generated keys, depends on other rows, or is just convenient startup data, the EF Core team now recommends UseSeeding and UseAsyncSeeding, introduced in EF Core 9. These run real SaveChanges against a live database, so the database generates the keys and the non-zero rule never applies.

// .NET 11, C# 14, EF Core 11.0.0
protected override void OnConfiguring(DbContextOptionsBuilder options)
    => options
        .UseSqlServer(connectionString)
        .UseSeeding((context, _) =>
        {
            if (!context.Set<Country>().Any())
            {
                // No Id set: the database generates it on SaveChanges. No error.
                context.Set<Country>().AddRange(
                    new Country { Name = "USA" },
                    new Country { Name = "Canada" });
                context.SaveChanges();
            }
        })
        .UseAsyncSeeding(async (context, _, ct) =>
        {
            if (!await context.Set<Country>().AnyAsync(ct))
            {
                context.Set<Country>().AddRange(
                    new Country { Name = "USA" },
                    new Country { Name = "Canada" });
                await context.SaveChangesAsync(ct);
            }
        });

UseSeeding is called from EnsureCreated and Migrate and from dotnet ef database update, even when there are no pending migrations. Implement both the sync and async overloads: the EF Core tooling calls the synchronous one, and it will silently skip seeding if only the async version exists. Because the seed body runs as application code with no design-time key requirement, omitting Id is exactly right here, the database assigns it.

Variants that produce the same error

The message wording is identical across these, so search traffic lands here for all of them. The cause is always the same (a HasData row missing an explicit store-generated key value), but the surface differs.

A different but easily confused error is The seed entity for entity type 'X' cannot be added because there was no value provided for the required property 'Y', where Y is a non-key required property like Name. That one means a [Required] or IsRequired() column was left null in a seed row, not that the key is missing. The fix there is to fill in the missing non-key property.

If the model never builds far enough to validate seed data because EF Core cannot even find a key for the type, you are looking at a different problem: see the fix for the entity type requires a primary key to be defined.

Why “just set Id = 0 explicitly” does not work

A tempting non-fix is to write new Country { Id = 0, Name = "USA" } and assume being explicit satisfies the validator. It does not. For a store-generated key, EF Core compares the value against the CLR default, and 0 is the default for int. The validator cannot distinguish your deliberate 0 from an unset field, so it still throws. The only values that pass are non-default ones: any non-zero int, any non-empty Guid, any non-default composite. If you genuinely need a row keyed 0, that is a sign the key should not be store-generated; mark it ValueGeneratedNever() and the validator stops applying the non-zero rule, because the key is now your responsibility entirely.

// .NET 11, C# 14, EF Core 11.0.0 - opt out of store generation, then 0 is allowed
modelBuilder.Entity<Country>(b =>
{
    b.Property(c => c.Id).ValueGeneratedNever();
    b.HasData(new Country { Id = 0, Name = "Unknown" });   // now valid
});

That is a real pattern for a sentinel “unknown” row, but be deliberate about it: once the key is ValueGeneratedNever, every future insert into that table must supply its own key, including runtime inserts from your application.

Sources

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Back