Cette page n'est pas encore traduite. La version anglaise est affichée ci-dessous.
FoundationDB .NET client 7.4.6
Released on 2026-09-XX.
In this release: CrystalJson serializes .NET 11 union types and formats and parses dates two to five
times faster with every Instant round-tripping to the nanosecond, CrystalXml serializes the
data-contract types of a referenced assembly, the emulator applies the cluster's 5 second history
window, and the native client ships as one package per FoundationDB branch.
Highlights
SnowBank:
- CrystalJson serializes .NET 11 union types, write side only, a work in progress.
- CrystalXml serializes a
[DataMember]whose setter is not public, instead of rejecting it withCXML0013. - Types without a parameterless constructor deserialize through their constructor, on both paths.
- Generator diagnostics respect
#pragma warningand.editorconfig. TuPackunpacks into a caller-suppliedRangebuffer, withCountItemsto size it.- Dates format and parse two to five times faster.
- An
Instantkeeps its nanoseconds, and every route spells a date the same way, an output change for DOM-built documents. - Fixed: date literals that threw, were rejected, or parsed the wrong value.
- String literals, decimals and Guid values serialize faster.
FoundationDB:
- The emulator applies the cluster's 5 second history window, a behavior change for tests that read at old versions.
FoundationDB.Client.Native: one package per FoundationDB branch,7.4.7and7.3.78, with a newosx-x64runtime.- Aspire: the 7.4 image tag is now 7.4.7.
- The test container replaces a stale container from another image.
- The experimental text index writes documents without reflection.
Dependencies:
Breaking changes lists the changes that can fail an existing test or restore.
SnowBank
CrystalJson serializes .NET 11 union types (work in progress)
Union support is a work in progress. This release covers the write side: a union serializes as its active case. Reading a union back is not implemented yet, and the behavior described here may change when the read side lands.
If you declared a C# union (a .NET 11 type marked [Union] that implements IUnion, which is what
the union keyword produces) and serialized it through CrystalJson, the output was the union struct
serialized from its public members, as for any struct the serializer has no rule for. In 7.4.6 a
union serializes as its active case value, with no envelope and no type discriminator; this matches
what System.Text.Json writes. The none state (Value is
null) writes JSON null. The reflection path and generated converters write the same output, and
a generated converter dispatches without boxing when every case has a TryGetValue(out TCase)
method.
public sealed record Cat(string Name);
public sealed record Dog(string Name);
public union Pet(Cat, Dog);
public sealed record Owner(string Name, Pet Pet);
[CrystalJsonConverter]
[CrystalSerializable(typeof(Pet))]
[CrystalSerializable(typeof(Owner))]
public static partial class AcmeSerializers { }
CrystalJson.Serialize(new Pet(new Cat("Felix")));
// => { "Name": "Felix" }
CrystalJson.Serialize(new Pet(new Dog("Rex")));
// => { "Name": "Rex" }
CrystalJson.Serialize(default(Pet));
// => null
AcmeSerializers.Owner.ToJsonText(new Owner("Alice", new Pet(new Cat("Felix"))));
// => { "Name": "Alice", "Pet": { "Name": "Felix" } }
The union types are detected by the attribute and interface names, so the net8.0 and net10.0
builds of the library include the code, but only a net11.0 application can declare a union.
Reading a union is not part of this release, and the output above has no discriminator to read it
back from. CrystalJson.Deserialize<Pet>("""{ "Name": "Felix" }""") returns a Pet whose Value
is null (the none state), with no error, and a type with a union member reads the member the same
way, on either path. For a union with scalar cases, such as union IntOrString(int, string), the
read throws JsonBindingException ("Cannot convert JSON Number to type 'IntOrString'"). Do not
round-trip a union through JSON until the read side ships.
CrystalXml serializes a [DataMember] whose setter is not public
If a [CrystalXmlOutput] container with the DataContractCompat format registered a
[DataContract] type from a referenced assembly, and the build failed with CXML0013 on a
[DataMember] property that has a private or internal setter, this is fixed. Such a property is
the usual shape of a data-transfer type: DataContractSerializer writes it and reads it through the
non-public setter, so the rejection was wrong for the write-only XML format.
The cause: when the type comes from a referenced assembly, the compiler imports its metadata
without non-public members, so the property looked like a get-only one to the generator, and a
get-only [DataMember] on a [DataContract] type is what CXML0013 rejects. The generator now
serializes such a property from its getter, which is all the XML write path needs. The same shape
declared in the same assembly was already accepted, and a fully private member stays invisible and
is not serialized.
With Invoice in a referenced Acme.Contracts assembly:
[DataContract]
public sealed class Invoice
{
public Invoice(string number, decimal total)
{
this.Number = number;
this.Total = total;
}
[DataMember]
public string Number { get; private set; }
[DataMember]
public decimal Total { get; internal set; }
}
The consuming assembly registers it:
[CrystalConverter]
[CrystalXmlOutput(CrystalXmlSerializerDefaults.DataContractCompat)]
[CrystalSerializable(typeof(Acme.Contracts.Invoice))]
public static partial class AcmeXmlSerializers { }
AcmeXmlSerializers.Invoice.ToXmlText(new Invoice("INV-2026-0042", 129.90m));
// 7.4.5: error CXML0013 on Number and Total
// 7.4.6: <Invoice xmlns="http://schemas.datacontract.org/2004/07/Acme.Contracts"><Number>INV-2026-0042</Number><Total>129.90</Total></Invoice>
Types without a parameterless constructor deserialize through their constructor
If you registered a positional record such as record Toy(string Name, int Size = 3) in a generated
container, the build failed inside the generated file with CS7036 ("There is no argument given that
corresponds to the required parameter 'Name'"), and CrystalJson.Deserialize<Toy> threw
JsonBindingException ("Failed to construct a new instance of type 'Toy'"). Both paths only called a
parameterless constructor. The workaround was to declare the members as init properties.
In 7.4.6 both paths bind a constructor, with the rule System.Text.Json applies. A type with a
parameterless constructor is still built with it, and nothing changes for it. Otherwise the
constructor is the one marked [JsonConstructor] (the System.Text.Json attribute, matched by
name), else the single public constructor whose every parameter matches a serialized member by name
(case-insensitive) and type.
Each parameter receives the value of its member. When the member is absent from the document, the
parameter's own default value applies (Size = 3), else the member's default. A member that no
parameter covers is assigned after construction, as before. Serialization is unchanged.
public sealed record Toy(string Name, int Size = 3);
public sealed class Bowl
{
public Bowl(string material) { this.Material = material; }
public string Material { get; }
public int Capacity { get; set; }
}
[CrystalJsonConverter]
[CrystalSerializable(typeof(Toy))]
[CrystalSerializable(typeof(Bowl))]
public static partial class AcmeSerializers { }
CrystalJson.Deserialize<Toy>("""{ "Name": "ball" }""");
// => Toy { Name = ball, Size = 3 }
AcmeSerializers.Toy.Unpack(JsonObject.Parse("""{ "Name": "ball", "Size": 5 }"""));
// => Toy { Name = ball, Size = 5 }
AcmeSerializers.Bowl.Unpack(JsonObject.Parse("""{ "Material": "steel", "Capacity": 2 }"""));
// => Material bound through the constructor, Capacity assigned after it
When no constructor can be called, the generator reports CJSON0027 (an error) on the type instead
of emitting code that does not compile: two constructors match and none is marked, [JsonConstructor]
is on two of them, or a parameter matches no member. The reflection path throws JsonBindingException
with the same reason at bind time, so a type that is only serialized is not affected.
Generator diagnostics respect #pragma warning and .editorconfig
The diagnostics of the JSON source generator (CJSON, CRYS and CXML ids) now respect
#pragma warning disable and dotnet_diagnostic.<id>.severity in .editorconfig; a project that
silenced one of them with <NoWarn> can move the suppression to the declarations it concerns.
The symptom: a #pragma warning disable CJSON0025 around a type had no effect, and neither did a
severity line in .editorconfig. Only a project-wide <NoWarn> worked, which silenced the
diagnostic for every type in the project. The cause: the generator reported each diagnostic with a
location that had a file path and a span but no syntax tree, and the compiler applies per-file
suppressions through the tree. The generator now binds each diagnostic to the tree of the
compilation it reports on. <NoWarn> is a compilation option, needed no tree, and still works.
The pragma wraps the declaration the diagnostic points at. CJSON0025 points at the type that
implements IJsonPackable, not at the container that registers it:
// GpsPoint implements its own format on purpose: no proxy is wanted
#pragma warning disable CJSON0025
public sealed record GpsPoint : IJsonPackable
{
public int Lat { get; init; }
public int Lon { get; init; }
JsonValue IJsonPackable.JsonPack(CrystalJsonSettings settings, ICrystalJsonTypeResolver resolver)
=> JsonString.Return($"{this.Lat}:{this.Lon}");
}
#pragma warning restore CJSON0025
[CrystalConverter]
[CrystalJsonOutput]
[CrystalSerializable(typeof(GpsPoint))]
public static partial class AcmeSerializers { }
Or, for one file or folder, in .editorconfig:
[*.cs]
dotnet_diagnostic.CJSON0025.severity = none
TuPack unpacks into a caller-supplied Range buffer
If a hot path unpacks keys and the profiler shows a Range[] allocation per call, these overloads
remove it. TuPack.Unpack(ReadOnlySpan<byte>) and TryUnpack allocate an array for the range of
each element. The new overloads take a Span<Range> that the caller supplies, typically a
stackalloc, and the returned SpanTuple is backed by that buffer, so the buffer must stay valid and
untouched while the tuple is used. CountItems and TryCountItems count the elements without
decoding them, so a caller can size the buffer up front.
Slice packed = TuPack.EncodeKey("orders", 42, "shipped");
Span<Range> buffer = stackalloc Range[8];
SpanTuple tuple = TuPack.Unpack(packed.Span, buffer);
tuple.Get<int>(1);
// => 42
TuPack.CountItems(packed.Span);
// => 3
When the buffer is too small, Unpack throws ArgumentException ("The buffer holds 2 ranges, but
the tuple has more than 2 items.") and TryUnpack returns false; the buffer is never replaced by a larger one.
IKeySubspace.Unpack(ReadOnlySpan<byte>, Span<Range>) is the subspace form of the same overload.
Two related changes on the decoding side need no caller change. An embedded tuple inside a packed
tuple now decodes as a SlicedTuple that views the packed bytes, instead of a list that boxed
every element. TuplePacker<IVarTuple>.Deserialize(Slice) returns that view over the caller's
Slice instead of a copy.
Dates format and parse two to five times faster
Date formatting was the last thing left at the top of a profile of a JSON-heavy service once everything
else had been tuned: every DateTime, DateTimeOffset and Instant cost as much as a dozen integers.
In 7.4.6 the ISO 8601 writer computes the time of day with two fixed-point multiplications instead of
four divisions, takes the calendar date from DateTime.Deconstruct, writes every digit pair from a
table, and formats in place in the output buffer instead of in a stack buffer copied afterwards. The
parsers read digits directly instead of calling int.TryParse on each field, and the DateTime and
Instant conversions take the span parser before the BCL and NodaTime parsers, which remain as fallbacks
for the spellings the span parser does not recognize.
Measured with BenchmarkDotNet on one x64 developer machine, .NET 10, Release, per value:
| Operation | 7.4.5 | 7.4.6 |
|---|---|---|
Write a DateTime with a fraction |
24.7 ns | 15.4 ns |
Write a DateTimeOffset |
29.4 ns | 17.8 ns |
Write an Instant |
25.1 ns | 15.0 ns |
Write a DateOnly |
11.4 ns | 6.5 ns |
JsonString.Return(Instant) |
89 ns, 320 bytes | 14 ns, 104 bytes (6 times faster) |
JsonValue.ToDateTime() |
88 ns | 17 ns (5 times faster) |
JsonValue.ToDateTimeOffset() |
61 ns | 23 ns |
JsonValue.ToInstant() |
86 ns, 256 bytes | 19 ns, no allocation (4.5 times faster) |
JsonValue.ToInstant() from a literal with an offset |
157 ns, 336 bytes | 22 ns, no allocation |
The writer paths allocate nothing, before and after. JsonString.Return(DateTime) keeps the same time
(20 ns) and the same two allocations, the string and the JsonString.
CrystalJsonParser.TryParseIso8601Instant is the new entry point of the Instant conversion; it is
public for code that parses instants outside a JSON document.
CrystalJsonParser.TryParseIso8601Instant("2025-06-16T19:46:17.934567891+02:00", out Instant parsed);
// => true, parsed == 2025-06-16T17:46:17.934567891Z
An Instant keeps its nanoseconds, and every route spells a date the same way
An Instant with nanoseconds below the tick came back changed after a round trip: the writer converted
it to a DateTime (100-nanosecond ticks) and wrote seven fraction digits, so …17.934567891Z was
written as …17.9345678Z and read back as a different instant. JsonString.Return(Instant) used the
NodaTime pattern and wrote nine digits, so the same value had two spellings depending on the route.
In 7.4.6 the writer formats the instant from its own nanoseconds. An instant with tick precision keeps its previous text; one with nanoseconds below the tick gets nine fraction digits, and both parse back to the same instant on every route.
The same alignment applies to DateTime and DateTimeOffset: JsonString.Return, JsonValue.FromValue
and JsonDateTime.ToJsonText now produce the text the writer produces. Before, JsonString.Return
used the BCL round-trip format, so a document built through the DOM and the same document serialized
from a CLR object differed on whole seconds and on unspecified midnights.
| Value | 7.4.5 | 7.4.6, every route |
|---|---|---|
Instant with 934567891 nanoseconds (text route) |
"…17:46:17.9345678Z" |
"…17:46:17.934567891Z" |
DateTime on a whole second, UTC (DOM route) |
"2025-06-16T17:46:17.0000000Z" |
"2025-06-16T17:46:17Z" |
DateTime at midnight, unspecified kind (DOM route) |
"2025-06-16T00:00:00.0000000" |
"2025-06-16" |
DateTime.MaxValue, UTC (DOM route) |
"9999-12-31T23:59:59.9999999Z" |
"9999-12-31T23:59:59.9999999" |
default(Instant), the Unix epoch (text route) |
"1970-01-01T00:00:00Z" |
"" |
Instant.MinValue (text route) |
"" |
"-9998-01-01T00:00:00Z" |
The last two rows settle which instant means "unset": default(Instant) is the empty string on every
route, the rule DateTime.MinValue already follows, and NodaTime's Instant.MinValue is a regular
date that round-trips. Before, the writer used Instant.MinValue as the sentinel, so that value came
back as the epoch.
var instant = Instant.FromUtc(2025, 6, 16, 17, 46, 17) + Duration.FromNanoseconds(934_567_891);
CrystalJson.Serialize(instant);
// => "2025-06-16T17:46:17.934567891Z"
CrystalJson.Deserialize<Instant>("\"2025-06-16T17:46:17.934567891Z\"") == instant;
// => true
JsonValue.FromValue(instant).ToJsonText() == CrystalJson.Serialize(instant);
// => true
var whole = new DateTime(2025, 6, 16, 17, 46, 17, DateTimeKind.Utc);
JsonValue.FromValue(whole).ToJsonText();
// => "2025-06-16T17:46:17Z" (7.4.5: "2025-06-16T17:46:17.0000000Z")
CrystalJson.Serialize(default(Instant));
// => "" (7.4.5: "1970-01-01T00:00:00Z")
Every spelling, old and new, parses back to the same value, so documents written by 7.4.5 read unchanged. What changes is the text: a test that compares the JSON text of a DOM-built document, an ETag or a hash computed over it, or a stored document compared byte for byte with a fresh serialization sees a difference for whole-second dates, unspecified midnights, and instants equal to the epoch. Update those expectations to the writer's text.
Fixed: date literals that threw, were rejected, or parsed the wrong value
Seven defects in the date parsers and the DOM factories, found by the round-trip tests added for this release. Each line gives the 7.4.5 behavior and the 7.4.6 behavior.
StringConverters.ParseDateTime("2025-06-16T17:46:17")returned 17:06:17: theYYYY-MM-DDTHH:mm:ssandYYYY-MM-DDTHH:mm:ssZcases passed the month as the minute. Now 17:46:17. The JSON conversions reach this parser only when the ISO 8601 parsers decline a literal, so a JSON date was affected only through a direct call."2016-12-31T23:59:60Z"(a leap second):JsonValue.ToDateTime(),ToDateTimeOffset()andToInstant()threwArgumentOutOfRangeException, andTryConvertToDateTimeOffsetthrew instead of returningfalse. Now theTrymethods returnfalseand the conversions throwFormatException, since neitherDateTimenorInstantcan represent second 60."2025-06-16T17:46:17+13:00"(Tonga, Samoa, Kiribati, Chatham): the offset parser rejected any offset beyond 12 hours, and the fallback parser dropped the offset and shifted the ticks. Now offsets up to 14 hours, theDateTimeOffsetlimit, parse exactly.JsonString.Return(DateTimeOffset)andJsonValue.FromValue(DateTimeOffset)compared the value withDateTime.MinValue, a conversion that applies the local time zone and throwsArgumentOutOfRangeExceptionon any machine east of Greenwich. Now the comparison is withDateTimeOffset.MinValue.JsonDateTime.ToJsonText()wrapped aDateTimeOffsetin single quotes,'2025-06-16T17:46:17+02:00', which is not JSON. Now double quotes."2025-06-16T17:46:+7Z"or" 7"inside a field:int.TryParseaccepted a sign or a space as part of a two-digit field. Now a field is two digits, nothing else."2025-06-16T17:46:17.934567891Z"into aDateTime: the BCL parser rejects more than seven fraction digits, so an instant written with nanoseconds failed to deserialize into aDateTimemember. Now the value truncates to the tick,…17.9345678.
String literals, decimals and Guid values serialize faster
On .NET 8 and later, the parser locates a string literal without escape sequences with one vectorized
scan for the closing quote or a backslash, then copies or interns it in one operation instead of one
character at a time. An escape, a non-ASCII byte in a UTF-8 document, or a missing closing quote falls
back to the previous character loop with nothing consumed, so accepted documents and their values are
unchanged. A decimal literal with at most 2^53 in its digits and at most 22 fraction digits, 1234.56
or 0.000001, is one exact division instead of a double.Parse of the literal; both operands are
exact doubles, so the result is the correctly rounded value the BCL returns. The writer formats a
Guid or a Uuid128, Uuid96, Uuid80 or Uuid64 in place between its quotes; Uuid96 and Uuid80
no longer allocate a string per value.
On a 614-byte order document (a Guid, two dates, five line items, three tags), parsing to the DOM
takes 1.7 µs from a string (2.7 µs in 7.4.5) and 2.0 µs from UTF-8 bytes (2.5 µs). Writing a Guid
takes 7.3 ns (9.0 ns). The netstandard2.0 build keeps the previous code paths.
FoundationDB
The emulator applies the cluster's 5 second history window
This changes the behavior of tests that read at an old version. Read the population paragraph below to see whether it concerns you.
The symptom: on a cluster, a read at a version older than about 5 seconds fails with
transaction_too_old, and the emulator (FoundationDB.FakeDb) did not implement this. Every
committed version stayed readable forever, memory grew with every commit of a long test run, and a
test that passed on the emulator could fail on the cluster with transaction_too_old.
In 7.4.6 the emulator implements the window. After each commit, the store runs a retention policy
that removes the versions outside the window; the default policy is
FdbSnapshotRetention.KeepWindow(FdbSnapshotRetention.DefaultWindow), a 5 second window measured on
the store's TimeProvider. A transaction that starts a read at a removed version fails with an
FdbException whose Code is FdbError.TransactionTooOld. A transaction that already took its
read version can still read at that version, however old it is. At commit, a transaction whose read
version is older than the oldest retained version fails with FdbError.TransactionTooOld when it has
write conflicts to check, as the cluster's resolver does with the history it has.
The window is measured on the store's TimeProvider, the one introduced in 7.4.5. With a
FakeTimeProvider, the window is virtual time: no version is removed while the test does not advance
its clock, and a test that advances the clock past 5 seconds removes versions at the instant it
chooses.
Who is affected: a test that sets a read version older than 5 seconds of store time, and a test
that keeps one transaction open for more than 5 seconds of store time and then commits writes. Under
the system clock, "store time" is wall-clock time, so a suite whose transactions are short sees no
change. Under a FakeTimeProvider, only a test that advances its clock past the window sees the
change. A test that fails on the emulator for one of these reasons would fail on a cluster too.
To keep the previous behavior, for example in a test that inspects every version of a run, select
FdbSnapshotRetention.KeepEverything where the store is created:
using var store = new FakeDbStore(retention: FdbSnapshotRetention.KeepEverything);
Or in the provider options, when the emulator is registered in a service collection:
services.AddFakeDb(
730,
FdbPath.Root,
configure: options => options.Retention = FdbSnapshotRetention.KeepEverything
);
With this policy, memory grows with every commit, as before. FakeDbProviderOptions.Retention is
ignored when the options also supply a Store: a shared store is configured where it is created.
Three policies are built in, and a custom one is a FdbSnapshotRetentionPolicy delegate:
| Policy | Retained versions |
|---|---|
FdbSnapshotRetention.KeepWindow(TimeSpan) |
the versions published within the window of the newest one, on the store's TimeProvider (the default, with DefaultWindow = 5 s) |
FdbSnapshotRetention.KeepLast(int) |
the most recent count versions |
FdbSnapshotRetention.KeepEverything |
every version (the behavior before 7.4.6) |
A custom policy receives a FdbSnapshotRetentionContext: the retained versions oldest first
(Count, the indexer, Head), the store's TimeProvider (Time), and Drop(version) to mark a
version for removal. The store calls the policy under its write lock after each commit;
Drop throws ArgumentException for the newest version.
FoundationDB.Client.Native: one package per FoundationDB branch
If you needed the native client for a 7.3 cluster, or could not tell which libfdb_c a 7.4.4.1
package contained, this is the change. A native client only connects to a cluster with the same major.minor,
and the package version now names the native version.
Through 7.4.5, the FoundationDB.Client.Native package had a version (7.4.4.1) that matched
neither the binding nor the native library exactly. In 7.4.6 the package version is the native
client version, and the package exists once per FoundationDB branch:
| Package version | Ships | For clusters |
|---|---|---|
FoundationDB.Client.Native 7.4.7 |
libfdb_c 7.4.7 |
7.4.x |
FoundationDB.Client.Native 7.3.78 |
libfdb_c 7.3.78 |
7.3.x |
A fourth version component is reserved for a repack of the same binaries. Both packages depend on
FoundationDB.Client 7.4.1 or later with no upper bound, so they restore next to a future major
version of the binding.
Pin the branch of your cluster with a floating patch:
dotnet add package FoundationDB.Client.Native --version 7.4.*
Or in the project file:
<ItemGroup>
<!-- other packages -->
<PackageReference Include="FoundationDB.Client" Version="7.4.6" />
<PackageReference Include="FoundationDB.Client.Native" Version="7.4.*" />
</ItemGroup>
Two platform notes:
- A new
osx-x64runtime contains the macOS x86_64 client, next toosx-arm64. - Since FoundationDB 7.4.5, the upstream
linux-x64client is compiled with AVX instructions. On an x86-64 host without AVX (a virtual machine on theqemu64,kvm64orx86-64-v2CPU model, or hardware older than 2011), the process is killed by an illegal-instruction signal the first time the library runs, before any transaction. Any CPU at thex86-64-v3level or above has AVX. Thewin-x64,linux-arm64,osx-arm64andosx-x64libraries have no such requirement.
Aspire: the 7.4 image tag is now 7.4.7
FdbAspireHostingExtensions.LatestVersion74 is now 7.4.7, so an AppHost that resolves a 7.4
version with the default FdbVersionPolicy.LatestPatch policy starts a 7.4.7 container. Since 7.4.5
the upstream 7.4 images are built with AVX and have no non-AVX twin, and since 7.4.6 they are
published for both amd64 and arm64, so Docker on an Apple Silicon host runs the arm64 image natively.
An amd64 host without AVX cannot run any 7.4 image since 7.4.5. The 7.3, 7.2 and 7.1 branches
still ship AVX and non-AVX pairs, and the same policy still selects their newest even (non-AVX) tag.
The test container replaces a stale container from another image
If a suite built on FoundationDB.Testing failed to start after an image tag change with
"FdbServer test container 'fdb-test-...' is conflicting with another container! Please delete the
old container", this is fixed. FdbServerTestContainer now detects a same-name container built from
another image tag, removes it and its volume, and creates a fresh one from the expected image. A
name conflict on a container of the same image that Testcontainers does not recognize is handled
the same way, keeping the volume. On .NET Framework, where the container is driven through the
docker command line, the same image check runs before an existing container is restarted.
The experimental text index writes documents without reflection
FdbTextIndex.IndexAsync (in FoundationDB.Layers.Experimental) now writes the stored document
with JsonObject.ToJsonText() instead of the reflection serializer. A trimmed or Native AoT
application that uses the layer no longer gets IL2026 and IL3050 warnings from it. The stored
bytes are unchanged.
Dependencies
The net11.0 build targets .NET 11 RC1. Its BCL and ASP.NET Core package references move from the
.NET 11 preview 7 packages (11.0.0-preview.7.26381.103) to the RC1 packages
(11.0.0-rc.1.26425.128), and the SDK pinned in global.json is 11.0.100-rc.1.26425.128. The
net10.0 build moves to the 10.0.12 servicing packages, and the net9.0 build to 9.0.20.
| Build | 7.4.5 | 7.4.6 |
|---|---|---|
| net11.0 | .NET 11 preview 7 (11.0.0-preview.7.26381.103) |
.NET 11 RC1 (11.0.0-rc.1.26425.128) |
| net10.0 | 10.0.11 |
10.0.12 |
| net9.0 | 9.0.18 |
9.0.20 |
| net8.0 (shared .NET wave) | 10.0.11 |
10.0.12 |
A consumer that builds the net11.0 target needs the .NET 11 RC1 SDK. The net10.0, net9.0 and net8.0
builds use released servicing packages and need no SDK change. A consumer that restores the net11.0
build on the preview 7 SDK fails to restore the 11.0.0-rc.1 packages: install the .NET 11 RC1 SDK.
Breaking changes
- The emulator applies the cluster's 5 second history window. A test that reads at a version
older than 5 seconds of store time, or commits writes from a transaction older than that, fails
with
FdbError.TransactionTooOld, as on a cluster. To keep the previous behavior:new FakeDbStore(retention: FdbSnapshotRetention.KeepEverything), oroptions.Retention = FdbSnapshotRetention.KeepEverythinginAddFakeDb. FoundationDB.Client.Nativehas a new version numbering:7.4.7and7.3.78are the versions of the native client each package ships, where the previous package was7.4.4.1. A project that pins the exact version7.4.4.1stays on the 7.4.4 client; move it to7.4.*.- Dates built through the DOM change their text.
JsonString.Return,JsonValue.FromValueandJsonDateTime.ToJsonTextnow write a whole second without a fraction ("…17:46:17Z", not"…17:46:17.0000000Z"), an unspecified midnight as a date only, and the extremes without a time zone. The writer writesdefault(Instant)as""(was"1970-01-01T00:00:00Z") and an instant with nanoseconds below the tick with nine fraction digits. Every spelling parses back to the same value; only tests, hashes and byte comparisons over the JSON text see a difference. - The net11.0 build targets .NET 11 RC1. A consumer that builds the net11.0 target on the .NET 11
preview 7 SDK fails to restore the
11.0.0-rc.1packages; install the .NET 11 RC1 SDK. The net10.0, net9.0 and net8.0 builds move to released servicing (10.0.12,9.0.20) and need no change. - Everything else in this release is additive.