首页 > 解决方案 > Npgsql - 使用“始终作为身份生成”

问题描述

我目前正在尝试使用 Npgsql(版本 3.1.3)使用官方文档(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 或更低),要么不起作用。

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

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

标签: npgsql

解决方案


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

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

如果由于某种原因,您需要GENERATED ALWAYS AS IDENTITY(而不是BY DEFAULT),则可以使用下面的 fluent 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; }
}

推荐阅读