Skip to content

Latest commit

 

History

History
81 lines (52 loc) · 6.33 KB

File metadata and controls

81 lines (52 loc) · 6.33 KB

Value Generation (auto-increment)

See the general EF Docs on value generation to better understand the concepts described here.

Serial and identity columns

The traditional PostgreSQL autoincrement mechanism is "serial columns". Serial columns are simply regular columns with default values coming from a sequence. The are defined with a datatype of serial, but this is simply a shorthand for creating a column of type bigint and tying it to a sequence. More detail on serial columns can be found in the PostgreSQL docs.

PostgreSQL 10 introduced new "identity columns", which conform to the SQL standard and provide some advantages over serial columns. With identity columns, the relationship between the column and its driving sequence is remembered, and further management of the column is much simpler. For an overview of the advantages of identity over serial, see this blog post.

The Npgsql EF Core provider allows you to choose which of the above you want on a property-by-property basis, or globally on your model. The following "value generation strategies" are available:

  • Serial: the traditional PostgreSQL serial column. This will create the column with the serial datatype.
  • Identity always: an identity column whose values are always generated at the database - you cannot provide values from your application. This will generate the clause GENERATED ALWAYS AS IDENTITY on your column.
  • Identity by default: an identity column whose values are by default generated at the database, but you can still override this behavior by providing values from your application. This will generate the clause GENERATED BY DEFAULT AS IDENTITY on your column.
  • Sequence HiLo: See below

To maintain backwards compatibility with existing EF Core models, serial columns are still the default: when ValueGeneratedOnAdd is specified on a short, int or long property, the Npgsql EF Core provider will automatically map it to a serial column. Note that EF Core will automatically recognize key properties by convention (e.g. a property called Id in your entity) and will implicitly set them to ValueGeneratedOnAdd, so if you set up a simple model with id columns, they will get created as serial columns.

To use identity columns for all value-generated properties on a new model, simply place the following in your context's OnModelCreating():

builder.ForNpgsqlUseIdentityColumns();

This will create make all keys and other properties which have .ValueGeneratedOnAdd() have Identity by default. You can use ForNpgsqlUseIdentityAlwaysColumns() to have Identity always, and you can also specify identity on a property-by-property basis with UseNpgsqlIdentityColumn() and UseNpgsqlIdentityAlwaysColumn().

If you set identity for existing columns, or even for your entire existing model, Npgsql will safely migrate you from serial to identity, preserving current sequence values. However, back up your database before you do this and test carefully, as migrating from identity to serial isn't supported at this time.

Warning

There was a significant and breaking change in 1.1. If you are upgrading from 1.0 and have existing migrations, please read the release notes.

Standard Sequence-Driven Columns

While serial sets up a sequence for you, you may want to manage sequence creation yourself. This can be useful for cases where you need to control the sequence's increment value (i.e. increment by 2), populate two columns from the same sequence, etc. Adding a sequence to your model is described in the general EF Core documentation; once the sequence is specified, you can simply set a column's default value to extract the next value from that sequence. Note that the SQL used to fetch the next value from a sequence differs across databases (see the PostgreSQL docs). Your models' OnModelCreating should look like this:

modelBuilder.HasSequence<int>("OrderNumbers")
	.StartsAt(1000)
	.IncrementsBy(5);

modelBuilder.Entity<Order>()
	.Property(o => o.OrderNo)
	.HasDefaultValueSql("nextval('\"OrderNumbers\"')");

HiLo Autoincrement Generation

One disadvantage of database-generated values is that these values must be read back from the database after a row is inserted. If you're saving multiple related entities, this means you must perform multiple roundtrips as the first entity's generated key must be read before writing the second one. One solution to this problem is HiLo value generation: rather than relying on the database to generate each and every value, the application "allocates" a range of values, which it can then populate directly on new entities without any additional roundtrips. When the range is exhausted, a new range is allocated. In practical terms, this uses a sequence that increments by some large value (100 by default), allowing the application to insert 100 rows autonomously.

To use HiLo, specify ForNpgsqlUseSequenceHiLo on a property in your model's OnModelCreating:

modelBuilder.Entity<Blog>().Property(b => b.Id).ForNpgsqlUseSequenceHiLo();

You can also make your model use HiLo everywhere:

modelBuilder.ForNpgsqlUseSequenceHiLo();

Guid/UUID Generation

By default, if you specify ValueGeneratedOnAdd on a Guid property, a random Guid value will be generated client-side and sent to the database.

If you prefer to generate values in the database instead, you can do so by specifying HasDefaultValueSql on your property. Note that PostgreSQL doesn't include any Guid/UUID generation functions, you must add an extension such as uuid-ossp or pgcrypto. This can be done by placing the following code in your model's OnModelCreating:

modelBuilder.HasPostgresExtension("uuid-ossp");
modelBuilder
	.Entity<Blog>()
	.Property(e => e.SomeGuidProperty)
	.HasDefaultValueSql("uuid_generate_v4()");

See the PostgreSQL docs on UUID for more details.

Computed Columns (On Add or Update)

PostgreSQL does not support computed columns.