Recently I've migrated from EF Npgsql Core 2.2 to the 3.1.1. All good, but now when using Add or AddRange methods from EF, a temporary Id isn't being sett on my entity.
Does anyone knows what can be the reason? Usually when I called add, a id value was sett with some random value like -1245343. I also tried to put the ValueGeneratedOnAdd for that table but no luck.
I'm going to put here the definition of a entity class, my DbContext, DI configuration for the DbContext, as well as the SQL script of the table.
The Entity
public class StatementHeader : RFBaseEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual int Id { get; set; }
public double TotalLocal { get; set; }
public double TotalForeign { get; set; }
.....
}
The DBContext
public abstract class EFBaseContext : DbContext
{
/// <summary>
/// Initializes a new instance of the <see cref="EFBaseContext"/> class.
/// Do not use for test only
/// </summary>
protected EFBaseContext()
{
}
protected EFBaseContext(DbContextOptions options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.RemovePluralizingTableNameConvention();
modelBuilder.Entity<Source>()
.HasMany(x => x.Templates)
.WithOne(x => x.Source);
modelBuilder.Entity<StatementHeader>()
.Property(i => i.Id)
.ValueGeneratedOnAdd();
modelBuilder.UseSerialColumns();
modelBuilder.RemoveCascadeDeleteConvention();
modelBuilder.HasDefaultSchema("public");
base.OnModelCreating(modelBuilder);
}
}
The Autofac DI container
var dbContextOptionsBuilder = new DbContextOptionsBuilder<EFCoreContext>()
.UseNpgsql(WebApiConfigurations.ConnectionString, (x=>x.SetPostgresVersion(9,6)));
builder.RegisterType<EFCoreContext>().As<DbContext>().WithParameter("options", dbContextOptionsBuilder.Options)
.InstancePerLifetimeScope();
Table Definition
CREATE TABLE public."StatementHeader" (
"Id" serial NOT NULL,
"CreatedOn" timestamp without time zone NOT NULL,
"ModifiedOn" timestamp without time zone NULL,
"CreatedByUserLogin" text NULL,
"ModifiedByUserLogin" text NULL,
"Description" text NULL,
"TotalLocal" double precision NOT NULL,
"TotalForeign" double precision NOT NULL,
"Date" timestamp without time zone NOT NULL,
"Comments" text NULL,
"TemplateId" integer NOT NULL,
"PaymentReceivedId" integer NULL,
CONSTRAINT "PK_StatementHeader" PRIMARY KEY ("Id"),
CONSTRAINT "FK_StatementHeader_PaymentReceived_PaymentReceivedId" FOREIGN KEY ("PaymentReceivedId") REFERENCES public."PaymentReceived" ("Id") ON DELETE RESTRICT,
CONSTRAINT "FK_StatementHeader_Template_TemplateId" FOREIGN KEY ("TemplateId") REFERENCES public."Template" ("Id") ON DELETE RESTRICT
);
This behavior was expected in 3.0?
Yes, it is one of the 3.0 Breaking Changes - Temporary key values are no longer set onto entity instances.
The proposed solutions there are:
- Not using store-generated keys.
- Setting navigation properties to form relationships instead of setting foreign key values.
- Obtain the actual temporary key values from the entity's tracking information. For example,
context.Entry(blog).Property(e => e.Id).CurrentValu
e will return the temporary value even thoughblog.Id
itself hasn't been set.
Option #1 doesn't make sense (apparently the affected places already use store generated keys).
Option #2 is preferable if you have navigation properties.
Option #3 is closer to the previous behavior, but requires access to the db context.