6

Entity Framework is not respecting my Identity columns. It insists on trying to insert a value into an Identity (auto-increment) column in my MS SQL DB, which is obviously an error since the DB is supposed to supply the value.

System.Data.SqlClient.SqlException: 'Cannot insert explicit value for identity column in table 'Assignee' when IDENTITY_INSERT is set to OFF.'

Why is it trying to do that? I've paired it down to a schema involving one table and one column:

CREATE TABLE [dbo].[Assignee](
  [AssigneeID] INT IDENTITY(-1, 1) NOT NULL
CONSTRAINT [Assignee$PrimaryKey] PRIMARY KEY CLUSTERED 
( [AssigneeID] ASC ))

After publishing this schema to my local DB I use Scaffold-DbContext to generate entity and context classes. The generated Assignee class contains just this public property.

public int AssigneeId { get; set; }

The context only refers to Assignee here:

modelBuilder.Entity<Assignee>(entity =>
{
  entity.Property(e => e.AssigneeId).HasColumnName("AssigneeID");
});

Searching around I see people claiming that for E.F. to respect Identity columns, the context should configure the property with ValueGeneratedOnAdd(). In other words, the line in the context class should read:

entity.Property(e => e.AssigneeId).HasColumnName("AssigneeID")
  .ValueGeneratedOnAdd();

I have two problems with this:

  1. I'm starting with an existing DB and generating entity classes. If I need ValueGeneratedOnAdd() then why isn't Scaffold-DbContext generating it?
  2. Even if I manually edit the generated context class and add ValueGeneratedOnAdd() it still doesn't work with the same error.

Elsewhere I see suggestions to use UseSqlServerIdentityColumn(). That also doesn't work for me. Points 1 and 2 still apply.

Any help would be greatly appreciate. Please don't suggest that I use IDENTITY_INSERT as that defeats the entire point of using auto-increment columns.

(I am using Entity Framework Core 2.2.3 and Microsoft SQL Server 14)

Fizzy
  • 352
  • 1
  • 2
  • 8
  • The EF Core model and database seem to be correct. The exception message indicates your code is adding `Assignee` with explicitly specified `AssigneeId` (other than `0`), in which case EF Core respects your explicit value (this is in order to support identity insert scenarios). Make sure `AssigneeId` is zero before calling `Add` method. – Ivan Stoev Apr 17 '19 at 02:28
  • 1
    The DatabaseGeneratedOption.Identity data annotation/fluent API configuration is missing from your identity column. Why it wasnt created by the scaffold I can't tell tho. Note that the explicit [Key] annotation that was suggested is not necessary, though it should work, since PK's are by default identity columns. – DevilSuichiro Apr 17 '19 at 08:34
  • Is `IDENTITY(-1, 1)` (-1) a typo? – Gert Arnold May 23 '20 at 20:08

5 Answers5

3

This works for me:

modelBuilder.Entity<Assignee>().Property(e => e.AssigneeId).UseIdentityColumn();

So UseIdentityColumn() is the key.

I'm using Microsoft.EntityFrameworkCore.SqlServer v3.1.8.

סטנלי גרונן
  • 2,917
  • 23
  • 46
  • 68
2
 protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Todo>(entity =>
            {
        entity.Property(x => x.Id)
                    .HasColumnName("id")
                    .HasColumnType("int")
                    .ValueGeneratedOnAdd()
                    **.UseIdentityColumn();**

    }

Try do this. Ef Core Dependency : Microsoft.EntityFrameworkCore.SqlServer

Fatih mert
  • 21
  • 2
2

Short version

We are getting and experiencing different results here one can reproduce the issue, others can not. My experience it depends on if the Id property's value is 0 or not.

Detailed version

My experience, that the default behavior (based on name convention) is definitely working, so in case you are naming your db entity's attribute (C# property) to Id or EntityNameId it should work. No C# entity class attributes neither OnModelCreating config is necessary. The same time if the issue is there neither No C# entity class attributes neither OnModelCreating config will fix it.

...because if the Id property's value is not 0, the generated SQL will contain the explicit field name and value, so we got the error. This is clearly and issue in EF core, but workaround is easy..

g.pickardou
  • 32,346
  • 36
  • 123
  • 268
  • This turns out to be the right answer. It treats the default value for the ID type (0) specially. If the value is 0 then it will generate it. If the value is not 0 then it will assume you've set it, try to insert it, and fail. I would guess it's not an issue but a dubious "feature". – Fizzy Oct 06 '20 at 15:46
  • In my case it was failing even if value is 0, I had to configure entity to ValueGenerateOnAdd. After that Id column was havng negative value, which I think is min value of int o long type. It worked after that. – Krunal Parmar Aug 18 '23 at 11:38
0

For DB first try adding [key] as a data annotation

With Data annotation

[Key]
public int AssigneeId { get; set; }

fluent API

modelBuilder.Entity<Assignee>()
        .HasKey(o => o.AssigneeId);

See here or here if you want to use fluent API

fuzzybear
  • 2,325
  • 3
  • 23
  • 45
  • I am generating my entity classes from an existing DB using scaffold. I really shouldn't be editing these classes. Also, why do you expect this will fix my problem? – Fizzy Apr 16 '19 at 23:19
  • Also, I have tried this with both the annotation and the fluent API and the behavior is still the same. – Fizzy Apr 16 '19 at 23:27
  • Yeah you can, use partial classes with MetadataTypeAttribute see here for example https://stackoverflow.com/questions/6131754/how-to-add-data-annotations-to-partial-class – fuzzybear Apr 17 '19 at 17:17
0

I've tried to reproduce this issue based on your example but it appears to work just fine. I did not use Scaffold though, just coded class and I tried the model creating code you had and it hasn't had an issue. I suspect there has to be more to this though because with just the "Assignee" class, EF convention is expecting an "Assignees" table, so I suspect there is more mapping being set up.

Tested with EF Core 2.0.3 and 2.2.4

DB: used the OP's script.

Entity:

[Table("Assignee")]
public class Assignee
{
    public int AssigneeId { get; set; }
}

I had to use the Table attribute to map to the table name.

Context:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Assignee>(entity =>
        {
            entity.Property(e => e.AssigneeId).HasColumnName("AssigneeID");
        });
    }

as-per OP comment.

Test:

   [Test]
    public void TestIncrement()
    {
        using (var context = new TestDbContext())
        {
            var newItem = new Assignee();
            context.Assignees.Add(newItem);
            context.SaveChanges();
        }
    }

Works as expected.

However, what I'd normally have for the entity:

[Table("Assignee")]
public class Assignee
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity), Column("AssigneeID")]
    public int AssigneeId { get; set; }
}

And then nothing for this column needed in the context OnModelCreating override.

I suspect that there is some additional configuration lurking somewhere given there is no mention of the table name issue, either manually added or via scaffold that is goofing up EF. I was full-on expecting EF to fail without the Key/DbGenerated attributes, but it seemed to work just fine.

Edit: Also tried this with scafolding running Scaffold-DbContext across the existing schema. Again, worked without an issue. For comparison against your tests:

Generated DbContext: (Unaltered save removing the warning and connection string details.)

public partial class AssigneeContext : DbContext
{
    public AssigneeContext()
    {
    }

    public AssigneeContext(DbContextOptions<AssigneeContext> options)
        : base(options)
    {
    }

    public virtual DbSet<Assignee> Assignee { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder.UseSqlServer("Data Source=machine\\DEV;Initial Catalog=Spikes;uid=user;pwd=password;MultipleActiveResultSets=True");
        }
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.HasAnnotation("ProductVersion", "2.2.4-servicing-10062");

        modelBuilder.Entity<Assignee>(entity =>
        {
            entity.Property(e => e.AssigneeId).HasColumnName("AssigneeID");
        });
    }
}

Generated Entity: (Unaltered)

public partial class Assignee
{
    public int AssigneeId { get; set; }
}

I did figure out why my table annotation was needed. EF Core (Not sure if applies to EF6 as well) was basing the convention for the table name on the DbSet variable name in the DbContext. I couldn't see any config difference with the scaffold generated context and my own, except the DbSet name. I renamed my original DbContext's DbSet name to "Assignee" and it worked without the Table attribute.

That said, based on the information present your code should work. Something is lurking in the details because this example does work so you will need to provide more detail about an example that definitely doesn't work in your case.

Steve Py
  • 26,149
  • 3
  • 25
  • 43
  • That is a pretty thorough example. Unfortunately I have a large existing DB and have to use scaffold. :-( It is very weird that your code needs the Table annotation and my does not. I see what you mean about "additional configuration" but I don't know what/where that would be. AFIK scaffold dumps all the generated code in the folder of your choice and the only thing I see is my context, and Assignee classes. – Fizzy Apr 16 '19 at 23:25
  • Can you put up the DbContext definition? Constructor, OnModelCreating, etc. Are there any classes anywhere that extend IEntityTypeConfiguration? EF (still) has 4 ways to configure entities, Attributes, Convention (automatic), IEntityTypeConfiguration and via modelBuilder in OnModelCreating and any mix of these can be employed in any given scenario. That yours seems to resolve the table name vs. the default convention without the attribute may be a good clue into the other behavior. – Steve Py Apr 17 '19 at 00:46
  • I've re-run the test using a scaffold built context & model and it still worked as expected. Updated the answer with details. – Steve Py Apr 17 '19 at 01:17