Skip to content

FileSystem/Temp persistence: GetAllKeysAsync returns wrong keys for double/DateTime keys under non-invariant cultures (key 1.5 comes back as 15 in de-DE) #46

Description

@matt-edmondson

What is wrong

The code that turns a key into a file name and the code that turns a file name back into a key use different cultures.

  • Writing: FileSystemPersistenceProvider.GetFilePath (Essentials.PersistenceProviders.FileSystem/FileSystemPersistenceProvider.cs:233) and the same method in Essentials.PersistenceProviders.Temp/TempPersistenceProvider.cs:257 both call key.ToString(), which formats with the current culture:
    string fileName = PersistenceProviderUtilities.GetSafeFileName(key.ToString()!) + _serializationProvider.FileExtension;
  • Reading: GetAllKeysAsync (FileSystem line 191, Temp line 188) calls PersistenceProviderUtilities.TryConvertToKey. For any type other than string, Guid and int, that uses Convert.ChangeType(value, typeof(TKey), CultureInfo.InvariantCulture) (Essentials/PersistenceProviderUtilities.cs:180), which parses with the invariant culture.

In a culture whose decimal separator or date order differs from invariant, a stored key is listed back as a different key. That key does not exist, so ExistsAsync and RetrieveAsync on it miss.

Second consequence: because the file name depends on the culture, data stored under one culture cannot be found when the process runs under another (1.5 is stored as 1,5.json under de-DE and looked up as 1.5.json under en-US).

Failure scenario

CultureInfo.CurrentCulture = new CultureInfo("de-DE");
var p = new FileSystemPersistenceProvider<double>(new NativeFileSystemProvider(), new JsonSerializationProvider(), dir);
await p.StoreAsync(1.5, "hello");
var keys = await p.GetAllKeysAsync();

Output:

files: 1,5.json
keys: 15
Exists(15)=False Retrieve=<null>

Expected: keys: 1.5, and retrieving that key returns "hello".

With DateTime keys, new DateTime(2026,3,4,5,6,7) is listed as 2026-04-03T05:06:07 (day and month swapped).

How it was verified

A scratch console app referencing the FileSystem persistence, Native filesystem and Json serialization projects, run against HEAD, produced the output above.

Suggested fix / acceptance criteria

  • Format keys with the invariant culture when building file names (IFormattable.ToString(null, CultureInfo.InvariantCulture) or Convert.ToString(key, CultureInfo.InvariantCulture)), using a round-trippable format ("R"/"O") where it matters. Share one helper between the FileSystem and Temp providers so they cannot drift.
  • Add a test that sets CurrentCulture to de-DE, stores double and DateTime keys, and asserts that GetAllKeysAsync returns exactly the stored keys and that RetrieveAsync finds them after CurrentCulture is switched to en-US.

Activity

  1. matt-edmondson commented on Sep 27, 2026

    @matt-edmondson
    ContributorAuthor

    Triage

    • Category: Bug
    • Priority: High. Under non-invariant cultures, persisted double/DateTime keys are listed back as different keys, and data stored under one culture can't be found under another. For a persistence provider, that means data effectively disappears.
    • Area / suggested owner: Essentials.PersistenceProviders.FileSystem and .Temp (GetFilePath), plus PersistenceProviderUtilities.TryConvertToKey
    • Duplicates / in progress: None found. No open PR.
    • Notes: Share one invariant, round-trippable key-to-filename helper between both providers. Changing the file-name format can orphan files already written under a non-invariant culture, so consider a fallback lookup or a release note.

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't workingreadyFully specified; implement as written

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions