Npgsql - 使用 "GENERATED ALWAYS AS IDENTITY"

Npgsql - Using "GENERATED ALWAYS AS IDENTITY"

我目前正在尝试使用 Npgsql(版本 3.1.3)将记录插入 table,并使用官方文档 (Npgsql.org) 生成身份。但我总是得到错误:

Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
 ---> Npgsql.PostgresException (0x80004005): 428C9: cannot insert into column "mitteilung_id"

我已经找到了几个关于这个主题的问题,但它们要么已经过时(版本 2 或更低),要么不起作用。

我的项目结构如下。 table 定义如下所示:

CREATE TABLE mitteilung
(
    mitteilung_id INTEGER GENERATED ALWAYS AS IDENTITY
        CONSTRAINT mitteilung_pk
            PRIMARY KEY,
    betreff       TEXT
        CONSTRAINT mitteilung_nn_betreff
            CHECK (betreff IS NOT NULL)
        CONSTRAINT mitteilung_ck_length_betreff
            CHECK (length(betreff) <= 100),
    nachricht     TEXT
        CONSTRAINT mitteilung_ck_length_nachricht
            CHECK (length(nachricht) <= 500)
        CONSTRAINT mitteilung_nn_nachricht
            CHECK (nachricht IS NOT NULL),
    erfasst_am    TIMESTAMP WITH TIME ZONE
        CONSTRAINT mitteilung_nn_erfasst_am
            CHECK (erfasst_am IS NOT NULL)
);

我定义的实体如下:

public class Mitteilung : ISlongooEntity
    {
        public int MitteilungId { get; set; }

...

我也试过给ID添加以下属性属性:

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]

在数据库上下文中,我测试了以下设置来解决问题。

modelBuilder.Entity<Mitteilung>()
                .Property(b => b.MitteilungId)
                .UseIdentityAlwaysColumn();

modelBuilder.Entity<Mitteilung>()
                .Property(b => b.MitteilungId)
 .Metadata.SetValueGenerationStrategy(NpgsqlValueGenerationStrategy.IdentityAlwaysColumn);

modelBuilder.Entity<Mitteilung>()
                .Property(b => b.MitteilungId)
.Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore);

但无论我使用哪种组合设置,在尝试保存实体时都会收到上述错误消息。我也不太明白为什么在进行更新时会尝试更新 ID。我做错了什么?

public Mitteilung Save(Mitteilung obj)
        {
            var addedObj = Context.Mitteilungen.Add(obj);
            // Context.Entry(obj).Property(x => x.MitteilungId).IsModified = false;
            Context.SaveChanges();
            return addedObj.Entity;
        }

下面的代码可以正常工作。

请注意,EF Core 会自动检测到 MitteilungIdMitteilung 的主键,并且由于它是一个 int,因此会将其设置为 GENERATED BY DEFAULT AS IDENTITY。换句话说,您不需要任何流畅的 API 调用 - 或者 [Key][DatabaseGenerated] 注释 - EF Core 将按照约定正确设置。

如果出于某种原因,您需要 GENERATED ALWAYS AS IDENTITY(而不是 BY DEFAULT),则可以使用下面的流畅 API 调用。

如果问题仍然存在,能否更改下面的代码示例以产生错误?

class Program
{
    static async Task Main(string[] args)
    {
        await using var ctx = new BlogContext();
        await ctx.Database.EnsureDeletedAsync();
        await ctx.Database.EnsureCreatedAsync();

        ctx.Blogs.Add(new Mitteilung { Name = "foo" });
        await ctx.SaveChangesAsync();
    }
}

public class BlogContext : DbContext
{
    public DbSet<Mitteilung> Blogs { get; set; }

    static ILoggerFactory ContextLoggerFactory
        => LoggerFactory.Create(b => b.AddConsole().AddFilter("", LogLevel.Information));

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseNpgsql("...")
            .EnableSensitiveDataLogging()
            .UseLoggerFactory(ContextLoggerFactory);

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Mitteilung>()
            .Property(b => b.MitteilungId)
            .UseIdentityAlwaysColumn();
    }
}

public class Mitteilung
{
    public int MitteilungId { get; set; }
    public string Name { get; set; }
}