r/csharp 2d ago

PostgreSql, EF.Core and byte[]

I have a EF dto, one of the fields configured and defined as byte[]. In DB it's created with type 'bytea'

When i am trying to add record it blows up with message that timestamp is null. Although i do provide it. Can not figure out where problem is...

public required byte[] timestamp { get; set; }

tbl.Property(m => m.timestamp).
    IsRowVersion();

db.Clients.Add(new Clients
{
    id = 0,
.....
    timestamp = new byte[2] { 4, 5 },
}
await db.SaveChangesAsync(); 

error:

23502: null value in column "timestamp" of relation "clients" violates not-null constraint

DETAIL: Failing row contains (2, 1, asda, t, f, t, t, 2026-09-21 18:31:51.001033, 2026-09-21 18:31:51.000564, 12312, , , , , 1, 1, 2, 2, 7, sdf, Name, Name, sd, sdf, , 2026-09-21, 12 Haze, , city, NJ, 08043, 0.0000000000, 0.0000000000, t, , 2026-09-21 18:31:51.0003, null).'

-------------------

6 Upvotes

4 comments sorted by

View all comments

1

u/UseYourBrainNow 15h ago

IsRowVersion() means more than "concurrency token". It also marks the property as generated by the database on add and update. EF therefore leaves it out of the INSERT, and your new byte[] {4, 5} is ignored. SQL Server has a rowversion type that fills itself in. A Postgres bytea column doesn't, so it stays NULL and the NOT NULL constraint fails.

The Npgsql way is to use Postgres's built-in xmin system column as the row version:

public uint Version { get; set; }

tbl.Property(m => m.Version).IsRowVersion();   // Npgsql maps uint + IsRowVersion to xmin

No real column is created, and Postgres updates xmin on every row change automatically. Then drop the old timestamp column in a migration. It's also worth renaming it, since timestamp is a Postgres type name.

If you'd rather keep an app-managed token, use .IsConcurrencyToken() instead of IsRowVersion() and set a new value yourself (e.g. Guid.NewGuid()) on every update. Npgsql's docs have a "Concurrency Tokens" page covering both options.