如何告诉Entity Framework我的ID列是自动增量的(AspNet Core 2.0 + PostgreSQL)?

21

代码很简单。Tag.cs实体:

public partial class Tag
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

HomeController.cs类:

public async Task<IActionResult> Index()
{
   tagRepository.Insert(new Tag 
               { 
                   Name = "name", 
                   Description = "description" 
               });
   await UnitOfWork.SaveChangesAsync();  // calls dbContext.SaveChangesAsync()

   return View();
}

TagRepository.cs 类:

    // Context it's a usual DBContext injected via Repository's constructor
    public virtual void Insert(TEntity item)
                => Context.Entry(item).State = EntityState.Added;

运行以下命令来创建标签表:

CREATE TABLE Tag (
    ID SERIAL PRIMARY KEY,
    Name text NOT NULL,
    Description text NULL
);

运行我的应用程序时,出现错误:

fail: Microsoft.EntityFrameworkCore.Database.Command[20102]
      Failed executing DbCommand (28ms) [Parameters=[@p0='?', @p1='?', @p2='?'], CommandType='Text', CommandTimeout='30']
      INSERT INTO "tag" ("id", "description", "name")
      VALUES (@p0, @p1, @p2);
Npgsql.PostgresException (0x80004005): 23505: duplicate key value violates unique constraint "tag_pkey"
   at Npgsql.NpgsqlConnector.<DoReadMessage>d__157.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at System.Runtime.CompilerServices.ValueTaskAwaiter`1.GetResult()
   at Npgsql.NpgsqlConnector.<ReadMessage>d__156.MoveNext()

从下面可以看到,实体框架试图向数据库发送id=0的值,但是我的数据库中已经存在一个记录与id=0相同,这就是为什么它抛出重复键错误。

我目前还没有找到任何答案,我的问题是:如何摆脱这个错误,并告诉实体框架我的id列是自动增加的,不需要更新它?

3个回答

28

你需要在这里使用 "ValueGenerationOnAdd()"。因为你遇到的问题已经在 GitHub 上报告过了,请查看以下链接。

https://github.com/npgsql/Npgsql.EntityFrameworkCore.PostgreSQL/issues/73

你可以从以下链接中找到有关生成值模式的更多信息。

Value generated on add

public classs SampleContext:DBContext{
public DbSet<Tag> Tag { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder){
    modelBuilder.Entity<Tag>()
        .Property(p => p.ID)
        .ValueGeneratedOnAdd();
 }
public class Tag{
  public int Id { get; set; }
  public string Name { get; set; }
  public string Description{get;set;}
  }
}

来源:- https://www.learnentityframeworkcore.com/configuration/fluent-api/valuegeneratedonadd-method

希望这能帮到你


2
我已经从现有的数据库中生成了上下文(使用命令 Scaffold-DbContext),有趣的是,所有实体中的 id 属性都使用 .Property(p => p.Id).ValueGeneratedNever() 生成。这可能是一个 bug,我猜.. 不管怎样,我已将其替换为 .ValueGeneratedOnAdd() 并且它可以工作了。感谢您提供的链接和解释。 - Andrey Kotov
我使用ValueGeneratedOnAdd()来自动创建ID,但它无法正常工作。如果您或@AndreyKotov能够看一下的话将不胜感激。请参阅https://stackoverflow.com/questions/59673778/how-to-avoid-null-value-in-column-violates-not-null-constraint-using-valuegen - Tim
请注意,如果您的属性名为Id或TagId,则无需手动设置ValueGeneratedOnAdd - 这是按照惯例为您完成的(请参见https://learn.microsoft.com/en-us/ef/core/modeling/keys?tabs=data-annotations和https://learn.microsoft.com/en-us/ef/core/modeling/generated-properties?tabs=data-annotations)。 - Shay Rojansky
答案中提到的源与问题无关。问题是关于Postgre SQL,而不是SQL Server。因此,该答案对我无效。 - manymanymore

5
根据Postgre SQL文档,以下示例展示了如何实现所需结果:
protected override void OnModelCreating(ModelBuilder modelBuilder)
   => modelBuilder.Entity<Blog>().Property(b => b.Id).UseIdentityAlwaysColumn();

.UseIdentityAlwaysColumn()无法解析..您使用的EF Core版本是多少? - anatol
顺便提一下,我已经尝试过 UseNpgsqlIdentityAlwaysColumn()UseNpgsqlSerialColumn() - 这些都不起作用。 - anatol
1
@anatol,你好。Microsoft.EntityFrameworkCore.Design - 3.1.3; Microsoft.EntityFrameworkCore.Tools - 3.1.3; Npgsql.EntityFrameworkCore.PostgreSQL - 3.1.3. - manymanymore

1
经过很多浪费时间的研究,我意外地解决了它。实际上,我的问题的解决方案在表面上,因为问题不在 EF 和它的东西上。在我的情况下,是 SEQUENCE。我的意思是,涉及的表有序列列,相关序列仅因某些副作用而更改。换句话说 - 序列从 1 重新启动,同时表已经具有值 1、2、3...等。这就是为什么 postgres 抛出“重复键”错误的原因。因此,解决方案是:
ALTER SEQUENCE "your_table_seq" RESTART WITH your_value;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接