Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,17 @@ public async Task StoreAsync<T>(TKey key, T obj, CancellationToken cancellationT
public async Task<T> RetrieveOrCreateAsync<T>(TKey key, CancellationToken cancellationToken = default) where T : new()
{
T? obj = await RetrieveAsync<T>(key, cancellationToken).ConfigureAwait(false);
return obj ?? new T();
if (obj is not null)
{
return obj;
}

// Stored before it is returned, as the interface documents and the other providers do: callers
// bootstrap a record with this, and one that was never written comes back as a fresh default on
// every call.
T newInstance = new();
await StoreAsync(key, newInstance, cancellationToken).ConfigureAwait(false);
return newInstance;
}

/// <inheritdoc/>
Expand Down
12 changes: 11 additions & 1 deletion Essentials.PersistenceProviders.Temp/TempPersistenceProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,17 @@ public async Task StoreAsync<T>(TKey key, T obj, CancellationToken cancellationT
public async Task<T> RetrieveOrCreateAsync<T>(TKey key, CancellationToken cancellationToken = default) where T : new()
{
T? obj = await RetrieveAsync<T>(key, cancellationToken).ConfigureAwait(false);
return obj ?? new T();
if (obj is not null)
{
return obj;
}

// Stored before it is returned, as the interface documents and the other providers do: callers
// bootstrap a record with this, and one that was never written comes back as a fresh default on
// every call.
T newInstance = new();
await StoreAsync(key, newInstance, cancellationToken).ConfigureAwait(false);
return newInstance;
}

/// <inheritdoc/>
Expand Down
36 changes: 36 additions & 0 deletions Essentials.Tests/PersistenceProviderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,42 @@ public void Temp_Properties()
}
}

// --- Cross-provider contract ---

[TestMethod]
public async Task RetrieveOrCreate_Stores_The_New_Instance_On_Every_Provider()
{
// Called through the interface, as consumers do: FileSystem and Temp declared their own public
// RetrieveOrCreateAsync that returned a default without storing it, so the same call persisted on
// InMemory, ConfigHome and DataHome but not on these two.
string fileSystemDir = Path.Combine(Path.GetTempPath(), "PersistenceTests_FS_" + Guid.NewGuid().ToString("N")[..8]);
Comment thread
github-code-quality[bot] marked this conversation as resolved.
Fixed
NativeFileSystemProvider fs = new();
JsonSerializationProvider serializer = new();
using TempPersistenceProvider<string> temp = new(fs, serializer, "PersistenceTests_Temp_" + Guid.NewGuid().ToString("N")[..8]);

try
{
IPersistenceProvider<string>[] providers =
[
CreatePersistence(),
new FileSystemPersistenceProvider<string>(fs, serializer, fileSystemDir),
temp,
];

foreach (IPersistenceProvider<string> persistence in providers)
{
TestData created = await persistence.RetrieveOrCreateAsync<TestData>("bootstrap", TestContext.CancellationToken).ConfigureAwait(false);
Assert.IsNotNull(created);
Assert.IsTrue(await persistence.ExistsAsync("bootstrap", TestContext.CancellationToken).ConfigureAwait(false), $"{persistence.ProviderName} did not store the new instance");
}
}
finally
{
CleanupDirectory(fileSystemDir);
temp.CleanupDirectory();
}
}

public sealed class TestData
{
public string Name { get; set; } = string.Empty;
Expand Down
Loading