# Npgsql 8.0 Release Notes

Npgsql version 8.0 is out and available on nuget.org.

##### Note

Npgsql 8.0 will be the last version to support .NET Framework (via .NET Standard 2.0). Starting with 9.0, Npgsql will only target .NET TFMs supported at release time (i.e. `net6.0`).

## NativeAOT and trimming support

Npgsql 8.0 now has 1st-class support for NativeAOT and trimming; the entire library has been properly annotated and is safe for use in applications. The majority of features have been made compatible with NativeAOT/trimming and can be used without issues, and most applications using Npgsql can be used as-is with NativeAOT/trimming without any changes. A few features which are incompatible require an explicit code opt-in, which generates a warning if used with NativeAOT/trimming enabled (see breaking change note).

Considerable effort has gone into reducing Npgsql's size footprint; a minimal Npgsql application using NativeAOT and trimming now takes only around 5MB of disk space. To allow users to achieve a minimal size footprint, NpgsqlSlimDataSourceBuilder has been introduced; unlike the standard NpgsqlDataSourceBuilder, this builder includes only the very minimum of functionality by default, and allows adding additional features via opt-ins. This allows a pay-per-play approach to application size, where developers can choose only the features they actually need for optimal size. For more information, see NpgsqlSlimDataSourceBuilder.

Making Npgsql NativeAOT/trimming-compatible was a far-reaching effort, affecting many parts of the driver and involving a rewrite of large parts of Npgsql's internals (leading to many other internal improvements). This huge task was done mainly by Nino Floris, with considerable contributions by Nikita Kazmin.

## OpenTelemetry metrics

Npgsql has emitted metrics for several versions, which provided aggregated metrics on various Npgsql interals; for example, it was possible to follow the state of the connection pool, or to track how many commands are being executed per second. Npgsql 8.0 improves on that by switching from the older EventCounter API to the newer System.Diagnostics.Metrics API, implementing OpenTelemetry metrics. To understand more about the different kinds of metrics APIs in .NET, see these docs.

The new metrics have several advantages over the old one; for one thing, they allow associating multiple dimensions to metrics, e.g. allowing Npgsql to cleanly emit pool-related metrics separately for each data source (or connection string) used in the application. The Npgsql metrics implement the experimental OpenTelemetry semantic conventions for database metrics - adding some additional useful ones - and will evolve as that specification stabilizes.

For more information, see the doc page on metrics.

## Register NpgsqlDataSource as a keyed DI service

.NET 8.0 introduced keyed services for dependency injection, allowing multiple services with the same CLR type to be registered in a single DI service provider. This is particularly useful when needing to contact multiple databases from your DI-enabled application:

    var builder = WebApplication.CreateBuilder(args);

    builder.Services
        .AddNpgsqlDataSource("Host=localhost;Database=CustomersDB;Username=test;Password=test", serviceKey: DatabaseType.CustomerDb)
        .AddNpgsqlDataSource("Host=localhost;Database=OrdersDB;Username=test;Password=test", serviceKey: DatabaseType.OrdersDb);

    var app = builder.Build();

    app.MapGet("/", async ([FromKeyedServices(DatabaseType.OrdersDb)] NpgsqlConnection connection)
        => connection.ConnectionString);

    app.Run();

    enum DatabaseType
    {
        CustomerDb,
        OrdersDb
    }

In this ASP.NET Minimal API application, two Npgsql data sources are registered in DI - one for a customers database, and another for an orders database. When a data source - or connections - needs to be injected somewhere, an enum is used as the service key, to distinguish which database is being requested (note that connections to both databases can be requested by the same function!).

For more information on registering Npgsql services in DI, see the documentation for Npgsql.DependencyInjection.

## Other features

- Allow using nullable value types with the generic `NpgsqlParameter<T>`, e.g. `NpgsqlParameter<int?>`.
- Introduce a non-caching password provider callback via NpgsqlDataSourceBuilder.UsePasswordProvider.
- Allow customizing System.Text.Json JsonSerializationOptions via NpgsqlDataSourceBuilder.ConfigureJsonOptions.
- Improvements and cleanup for networking type mappings:
  - In addition to .NET IPAddress, PostgreSQL `inet` can also mapped to be mapped to NpgsqlInet, which is an immutable struct containing both IP and netmask components.
  - PostgreSQL `cidr` is now mapped to the newly-introduced NpgsqlCidr. The mapping to `ValueTuple<IPAddress, int>` has been removed.
- Allow providing the root certificate programmatically via the new NpgsqlDataSourceBuilder.UseRootCertificate

Version 8.0 contains many other smaller features and bug fixes, see the 8.0.0 milestone for the full list of issues.

## Breaking changes

### JSON POCO and other dynamic features now require an explicit opt-in

Npgsql 8.0 is fully compatible with NativeAOT and trimming (see above). While most driver capabilities have been made to work in those profiles, certain features involve dynamic coding practices and are incompatible with NativeAOT and/or trimming - at least for now. As a result, these features now require explicit opt-ins (annotated to be incompatible with NativeAOT/trimming), which you must add either on your NpgsqlDataSourceBuilder or on NpgsqlConnection.GlobalTypeMapper:

PostgreSQL type | Default .NET type
--- | ---
JSON POCO mapping, JsonNode and subtypes | EnableDynamicJson
Unmapped enums, ranges, multiranges | EnableUnmappedTypes
Read PostgreSQL records as .NET tuples | EnableRecordsAsTuples

Existing code using the above features will start throwing exceptions after upgrading to Npgsql 8.0; the exceptions provide explicit guidance on how to add the opt-ins.

### `SSL Mode=Require` no longer validates certificates

tl;dr use `SSL Mode=VerifyCA` or `VerifyFull` in order to validate certificates provided by PostgreSQL.

In versions of Npgsql older than 6.0, specifying `SSL Mode=Require` made Npgsql validate the SSL/TLS certificate provided by PostgreSQL. This did not align with the meaning of "require" in PostgreSQL and other clients, where it simply means that SSL/TLS is required, but without certificate validation. To align with the standard PostgreSQL meaning, starting with Npgsql 6.0 `VerifyCA` or `VerifyFull` must be specified to validate the certificate.

To prevent existing usage of `Require` to silently stop validating, Npgsql 6.0 and 7.0 forced `Trust Server Certificate=true` to be specified; this made users aware of the change, guiding them to either switch to `VerifyCA`/`VerifyFull` (if they want validation) or to add `Trust Server Certificate=true` (if they don't). After two major versions, we are now removing the requirement to specify `Trust Server Certificate=true` with `SSL Mode=Require`; the latter will behave in the standard PostgreSQL way and will not verify certificates.

For more context, see #3988.

### IList<T> mapping now requires a generic NpgsqlParameter<T>

Previous versions of Npgsql allowed writing arbitrary list types as PostgreSQL array, as long as they implemented the `IList<T>` interface:

    await using var command = new NpgsqlCommand("SELECT $1", conn)
    {
        Parameters = { new NpgsqlParameter { Value = new ReadOnlyCollection<int>(new List<int> { 1, 2, 3 }) } }
    };
    await using var reader = await command.ExecuteReaderAsync();

This capability has been removed; supporting it required a costly reflection check, which also would be difficult to implement with trimming enabled, potentially increasing binary size in an unacceptable way. As a mitigation, you can instead use the generic `NpgsqlParameter<T>` - typed with `IList<T>` - to do the same:

    await using var command = new NpgsqlCommand("SELECT $1", conn)
    {
        Parameters = { new NpgsqlParameter<IList<int>> { Value = new ReadOnlyCollection<int>(new List<int> { 1, 2, 3 }) } }
    };
    await using var reader = await command.ExecuteReaderAsync();

### `cidr` now maps to `NpgsqlCidr` instead of `ValueTuple<IPAddress, int>`

As part of improving Npgsql's support for the PostgreSQL network mappings (see above), the PostgreSQL `cidr` type now maps to the newly-introduced NpgsqlCidr, and can no longer be mapped to `ValueTuple<IPAddress, int>`.

### Obsoletions and obsolete API removals

- The `Integrated Security` connection parameter has been removed. It was never actually necessary - simply stop setting it.
- `NpgsqlTsVector.Parse()` and `NpgsqlTsQuery.Parse()` are now obsolete. These methods attempted to mimic the behavior of the PostgreSQL `to_tsvector` and `to_tsquery` functions, but could only do so partially and in problematic ways. Use the PostgreSQL functions instead.
- The parsing functions on the built-in geometry types (NpgsqlPoint, NpgsqlBox etc.) have been removed; similarly, they partially replicated PostgreSQL parsing functionality client-side and had issues.
- `NpgsqlLargeObjectManager` and `NpsgqlLargeObjectStream` are now obsolete. These types were very rarely-used, provided only a thin wrapper over easily-accessible PostgreSQL large-object functions, and limited usage in various ways (e.g. they didn't allow batching). Call the PostgreSQL large-object functions directly.
- The `Internal Command Timeout` connection string parameter has been obsoleted.
- `NpgsqlDbType.TimestampTZ` and `NpgsqlDbType.TimeTZ` were obsoleted many releases ago, and were finally removed. Use `NpgsqlDbType.TimestampTz` and `NpgsqlDbType.TimeTz` instead.

### Executing a void-returning function returns .NET null instead of DBNull

Previously, executing a void-returning returned `DBNull.Value`:

    var command = new NpgsqlCommand("SELECT pg_sleep(10)", connection);
    var result = await command.ExecuteScalarAsync();

Before 8.0, `result` had the value `DBNull.Value`; this has been changed in 8.0 to be .NET `null`. This is more correct (as there are no results, rather than a result containing NULL), aligns with ADO.NET standard practices and with other drivers.

### Plugin APIs have been changed for NativeAOT/trimming support

As part of the effort to make Npgsql compatible with NativeAOT and trimming, the plugin API was changed in fundamental, breaking ways. Although this API never had the stability guarantees of a true public API (it was and still is in an Internal namespace), external plugins which were developed with it will require adjustments.

## Contributors

Thank you very much to the following people who have contributed to the individual 8.0.x. releases.

### Milestone 8.0.0

Contributor | Assigned issues
--- | ---
@NinoFloris | 43
@vonzshik | 23
@roji | 20
@manandre | 4
@BogdanYarotsky | 1
@Brar | 1
@erikdesj | 1
@SoftStoneDevelop | 1
@sonquer | 1
@yucelkivanc-hepsiburada | 1