Npgsql - Using "GENERATED ALWAYS AS IDENTITY"

0

I'm currently trying to use Npgsql (version 3.1.3) to insert a record into a table with a generated identity using the official documentation (Npgsql.org) . But I always get the error:

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"

I have already found several questions about this topic, but they are either outdated (version 2 or lower) or do not work.

My project is structured as follows. The table definition looks like this:

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

I have defined the entity as follows:

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

...

I have also tried to add the following attributes to the ID property:

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]

In the DB context I have tested the following settings to solve the problem.

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

But no matter in which combination I use the settings, I get the above mentioned error message when trying to save an entity. I also don't quite understand why an attempt is made to update the ID at all when an update is made. What am I doing wrong?

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
asked on Stack Overflow May 30, 2020 by Sven M.

1 Answer

1

The code below does work correctly.

Note that EF Core will automatically detect that MitteilungId is the primary key for Mitteilung, and since it's an int, will set it up as GENERATED BY DEFAULT AS IDENTITY. In other words, you don't need any of the fluent API calls - or the [Key] or [DatabaseGenerated] annotations - EF Core will set things up correctly by convention.

If, for some reason, you need to have GENERATED ALWAYS AS IDENTITY (instead of BY DEFAULT), then the fluent API call below can be used.

If you're still having an issue, can you please change the code sample below to produce the error?

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; }
}
answered on Stack Overflow May 30, 2020 by Shay Rojansky

User contributions licensed under CC BY-SA 3.0