实体框架核心不尊重标识列

问题描述 投票:2回答:2

实体框架不尊重我的Identity专栏。它坚持尝试在我的MS SQL DB中的Identity(自动增量)列中插入一个值,这显然是一个错误,因为DB应该提供该值。

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

为什么要这样做呢?我已经将它配对到涉及一个表和一列的模式:

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

在将此模式发布到我的本地数据库之后,我使用Scaffold-DbContext生成实体和上下文类。生成的Assignee类仅包含此公共属性。

public int AssigneeId { get; set; }

上下文仅指Assignee

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

搜索周围我看到人们声称要让E.F.尊重Identity列,上下文应该使用ValueGeneratedOnAdd()配置属性。换句话说,上下文类中的行应为:

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

我有两个问题:

  1. 我从一个现有的DB开始并生成实体类。如果我需要ValueGeneratedOnAdd()那么为什么不是Scaffold-DbContext产生它?
  2. 即使我手动编辑生成的上下文类并添加ValueGeneratedOnAdd(),它仍然无法使用相同的错误。

在其他地方,我看到使用UseSqlServerIdentityColumn()的建议。这对我也不起作用。第1点和第2点仍然适用。

任何帮助都会非常感激。请不要建议我使用IDENTITY_INSERT,因为它击败了使用自动增量列的整个点。

(我使用的是Entity Framework Core 2.2.3和Microsoft SQL Server 14)

c# entity-framework tsql entity-framework-core auto-increment
2个回答
0
投票

对于DB,首先尝试添加[key]作为数据注释

使用数据注释

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

流畅的API

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

如果您想使用流畅的API,请参阅herehere


0
投票

我试图根据你的例子重现这个问题,但似乎工作得很好。我没有使用Scaffold,只是编写了类,我尝试了创建代码的模型并且没有出现问题。我怀疑还有更多内容,因为只有“Assignee”类,EF约定期待一个“Assignees”表,所以我怀疑有更多的映射被设置。

使用EF Core 2.0.3和2.2.4进行测试

DB:使用了OP的脚本。

实体:

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

我不得不使用Table属性映射到表名。

语境:

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

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

按OP评论。

测试:

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

按预期工作。

但是,我通常对该实体拥有的东西:

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

然后在上下文OnModelCreating覆盖中不需要此列。

我怀疑有一些额外的配置潜伏在某处,因为没有提到表名问题,无论是手动添加还是通过脚手架填充EF。我完全期待EF在没有Key / DbGenerated属性的情况下失败,但它似乎工作得很好。

编辑:还尝试使用脚手架跨现有架构运行Scaffold-DbContext。再次,没有问题的工作。为了与您的测试进行比较:

生成的DbContext :(未更改保存删除警告和连接字符串详细信息。)

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

生成的实体:(未改变)

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

我确实弄清楚为什么需要我的表注释。 EF Core(不确定是否也适用于EF6)是基于DbContext中DbSet变量名的表名约定。除了DbSet名称之外,我看不到与scaffold生成的上​​下文和我自己的上下文的任何配置差异。我将我原来的DbContext的DbSet名称重命名为“Assignee”,它没有Table属性。

也就是说,基于所提供的信息,您的代码应该可行。有些东西潜伏在细节中,因为这个例子确实有效,所以你需要提供一些在你的情况下肯定不起作用的例子的更多细节。

© www.soinside.com 2019 - 2024. All rights reserved.