实体框架不尊重我的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();
我有两个问题:
ValueGeneratedOnAdd()
那么为什么不是Scaffold-DbContext
产生它?ValueGeneratedOnAdd()
,它仍然无法使用相同的错误。在其他地方,我看到使用UseSqlServerIdentityColumn()
的建议。这对我也不起作用。第1点和第2点仍然适用。
任何帮助都会非常感激。请不要建议我使用IDENTITY_INSERT
,因为它击败了使用自动增量列的整个点。
(我使用的是Entity Framework Core 2.2.3和Microsoft SQL Server 14)
我试图根据你的例子重现这个问题,但似乎工作得很好。我没有使用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属性。
也就是说,基于所提供的信息,您的代码应该可行。有些东西潜伏在细节中,因为这个例子确实有效,所以你需要提供一些在你的情况下肯定不起作用的例子的更多细节。