Table of Contents

9.0 Release Notes

Npgsql.EntityFrameworkCore.PostgreSQL version 9.0 is out and available on nuget.org.

Improved, unified configuration experience

The Npgsql EF provider is built on top of the lower-level Npgsql ADO.NET provider; the configuration interface between these two layers was less than ideal, and configuration been more difficult than it should have been. For version 9.0, the configuration experience has been considerably improved.

Since version 7, the Npgsql ADO.NET provider has been moving to NpgsqlDataSource as the preferred way of configuring connections and obtaining them. At the EF level, it has been possible to pass an NpgsqlDataSource instance to UseNpgsql(); but this required that the user separately configure a data source and manage it. In addition, features such as plugins and enums require support from both the EF and ADO.NET layers, forcing users to perform multiple setup actions at the different layers.

With version 9, UseNpgsql() becomes a single point for configuration, for both the EF and ADO.NET levels. EF can now internally set up an NpgsqlDataSource, automatically applying all the necessary configuration to it, and also exposes an API to allow users to apply arbitrary configuration to it as well:

builder.Services.AddDbContextPool<BloggingContext>(opt =>
    opt.UseNpgsql(
        builder.Configuration.GetConnectionString("BloggingContext"),
        o => o
            .SetPostgresVersion(13, 0)
            .UseNodaTime()
            .MapEnum<Mood>("mood")
            .ConfigureDataSource(dataSourceBuilder => dataSourceBuilder.UseClientCertificate(certificate))));

In the above code, the following configuration gestures are performed:

  1. SetPostgresVersion() is an EF-only option to produce SQL for PostgreSQL version 13 (avoiding newer incompatible features)
  2. UseNodaTime(), adds a plugin allowing use of NodaTime for date/time type mapping. This also requires an ADO.NET NodaTime plugin which needed to be configured separately, but this is now done automatically.
  3. MapEnum() maps a .NET enum type. Like UseNodaTime(), this also used to require a separate ADO.NET configuration gesture, but is now done automatically. As an added bonus, doing this now also adds the enum to the model, causing the enum to be created in the database via EF's migrations.
  4. ConfigureDataSource() exposes an NpgsqlDataSourceBuilder, which you can use to configure arbitrary ADO.NET options. In this example, the certificate is defined for the TLS authentication process.

For more information, see the getting started docs.

Improved configuration for enums and plugins

Previously, configuration around enums and plugins (NodaTime, NetTopologySuite) was complicated, requiring multiple setup actions at both the EF and the lower-level Npgsql layers. EF 9.0 improves the configuration story, allowing you to configure enums and plugins via a single EF gesture:

builder.Services.AddPooledDbContext<MyContext>(options => options.UseNpgsql(
    "<connection string>",
    o => o.MapEnum<Mood>("mood")));

This takes care of everything - EF configuration, lower-level Npgsql configuration and even the addition of the enum to the EF model, which ensures that the enum is created in the database in EF migrations.

See the enum, NodaTime and NetTopologySuite documentation for more details.

UUIDv7 GUIDs are generated by default

When your entity types have a Guid key, EF Core by default generates key values for new entities client-side - in .NET - before inserting those entity types to the database; this can be better for performance in some situations. Before version 9.0, the provider generated random GUIDs (version 4) by calling the .NET Guid.NewGuid() function. Unfortunately, random GUIDs aren't ideal for database indexing and can cause performance issues.

Version 9.0 of the provider now generates the recently standardized version 7 GUIDs, which is a sequential GUID type that's more appropriate for database indexes and improves their performance. This new behavior is on by default and takes effect simply by upgrading the provider version.

See this post for more details and performance numbers on random vs. sequential GUIDs.

Thanks to @ChrisJollyAU and @Timovzl for contributing this improvement!

Other new features

  • Translate array.Where(i => i != x) to array_remove(array, x)
  • Translate DateOnly.DayNumber, DateOnly.FromDayNumber() and simplify dateOnly1.DayNumber - dateOnly2.DayNumber to dateOnly1 - dateOnly2.
  • Map the PostgreSQL jsonpath type to .NET string; this allows mapping to jsonpath columns.

See the 9.0.0 milestone for the full list of Npgsql EF provider issues.

Breaking changes

Enum mappings must now be configured at the EF level

Previously, enum configuration involved mapping the enum at the lower-level Npgsql layer (either via NpgsqlDataSourceBuilder.MapEnum or via NpgsqlConnection.GlobalTypeMapper.MapEnum); the EF provider automatically picked this configuration up for the EF-level setup. Unfortunately, this design created numerous issues and bugs.

As part of the improved enum configuration story in version 9.0 (see above), enums must now be configured at the EF level; although this is a breaking change for existing applications, it usually results in simplified setup code and fixes various bugs and problematic behavior.

If your application calls UseNpgsql with a simple connection string (rather than an NpgsqlDataSource), it simply needs to add a MapEnum call there:

builder.Services.AddDbContext<MyContext>(options => options.UseNpgsql(
    "<connection string>",
    o => o.MapEnum<Mood>("mood")));

All other setup code - the MapEnum call on NpgsqlConnection.GlobalTypeMapper and the HasPostgresEnum call in OnModelCreating - can be removed.

If your application passes an NpgsqlDataSource to UseNpgsql, it also needs to add the MapEnum call as above; but the MapEnum call on NpgsqlDataSourceBuilder must also be kept.

See the enum documentation for more information.

Contributors

A big thank you to all the following people who contributed to the 9.0 release!

Milestone 9.0.0

Contributor Assigned issues
@roji 34
@ChrisJollyAU 1
@Timovzl 1