Skip to content
This repository was archived by the owner on Dec 26, 2025. It is now read-only.

Commit 9aad9bd

Browse files
authored
Merge pull request #46 from Blazored/setitemasstring
Added SetItemAsString[Async] methods
2 parents 6ecbc31 + da46b32 commit 9aad9bd

6 files changed

Lines changed: 473 additions & 4 deletions

File tree

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
# Blazored SessionStorage
66
Blazored SessionStorage is a library that provides access to the browsers session storage APIs for Blazor applications. An additional benefit of using this library is that it will handle serializing and deserializing values when saving or retrieving them.
77

8-
## Breaking Change (v1 > v2): JsonSerializerOptions
8+
## Breaking Change (v1 > v2)
9+
10+
### JsonSerializerOptions
911
From v4 onwards we use the default the `JsonSerializerOptions` for `System.Text.Json` instead of using custom ones. This will cause values saved to session storage with v3 to break things.
1012
To retain the old settings use the following configuration when adding Blazored SessionStorage to the DI container:
1113

@@ -21,6 +23,10 @@ builder.Services.AddBlazoredSessionStorage(config =>
2123
);
2224
```
2325

26+
### SetItem[Async] method now serializes string values
27+
Prior to v2 we bypassed the serialization of string values as it seemed a pointless as string can be stored directly. However, this led to some edge cases where nullable strings were being saved as the string `"null"`. Then when retrieved, instead of being null the value was `"null"`. By serializing strings this issue is taken care of.
28+
For those who wish to save raw string values, a new method `SetValueAsString[Async]` is available. This will save a string value without attempting to serialize it and will throw an exception if a null string is attempted to be saved.
29+
2430
## Installing
2531

2632
To install the package add the following line to you csproj file replacing x.x.x with the latest version number (found at the top of this file):
@@ -117,6 +123,7 @@ The APIs available are:
117123

118124
- asynchronous via `ISessionStorageService`:
119125
- SetItemAsync()
126+
- SetItemAsStringAsync()
120127
- GetItemAsync()
121128
- GetItemAsStringAsync()
122129
- RemoveItemAsync()
@@ -127,6 +134,7 @@ The APIs available are:
127134

128135
- synchronous via `ISyncSessionStorageService` (Synchronous methods are **only** available in Blazor WebAssembly):
129136
- SetItem()
137+
- SetItemAsString()
130138
- GetItem()
131139
- GetItemAsString()
132140
- RemoveItem()

src/Blazored.SessionStorage/ISessionStorageService.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Threading.Tasks;
33

44
namespace Blazored.SessionStorage
@@ -60,6 +60,14 @@ public interface ISessionStorageService
6060
/// <returns>A <see cref="ValueTask"/> representing the completion of the operation.</returns>
6161
ValueTask SetItemAsync<T>(string key, T data);
6262

63+
/// <summary>
64+
/// Sets or updates the <paramref name="data"/> in session storage with the specified <paramref name="key"/>. Does not serialize the value before storing.
65+
/// </summary>
66+
/// <param name="key">A <see cref="string"/> value specifying the name of the storage slot to use</param>
67+
/// <param name="data">The string to be saved</param>
68+
/// <returns></returns>
69+
ValueTask SetItemAsStringAsync(string key, string data);
70+
6371
event EventHandler<ChangingEventArgs> Changing;
6472
event EventHandler<ChangedEventArgs> Changed;
6573
}

src/Blazored.SessionStorage/ISyncSessionStorageService.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22

33
namespace Blazored.SessionStorage
44
{
@@ -56,6 +56,14 @@ public interface ISyncSessionStorageService
5656
/// <param name="data">The data to be saved</param>
5757
void SetItem<T>(string key, T data);
5858

59+
/// <summary>
60+
/// Sets or updates the <paramref name="data"/> in session storage with the specified <paramref name="key"/>. Does not serialize the value before storing.
61+
/// </summary>
62+
/// <param name="key">A <see cref="string"/> value specifying the name of the storage slot to use</param>
63+
/// <param name="data">The string to be saved</param>
64+
/// <returns></returns>
65+
void SetItemAsString(string key, string data);
66+
5967
event EventHandler<ChangingEventArgs> Changing;
6068
event EventHandler<ChangedEventArgs> Changed;
6169
}

src/Blazored.SessionStorage/SessionStorageService.cs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Text.Json;
33
using System.Threading.Tasks;
44
using Blazored.SessionStorage.Serialization;
@@ -32,6 +32,24 @@ public async ValueTask SetItemAsync<T>(string key, T data)
3232
RaiseOnChanged(key, e.OldValue, data);
3333
}
3434

35+
public async ValueTask SetItemAsStringAsync(string key, string data)
36+
{
37+
if (string.IsNullOrWhiteSpace(key))
38+
throw new ArgumentNullException(nameof(key));
39+
40+
if (data is null)
41+
throw new ArgumentNullException(nameof(data));
42+
43+
var e = await RaiseOnChangingAsync(key, data).ConfigureAwait(false);
44+
45+
if (e.Cancel)
46+
return;
47+
48+
await _storageProvider.SetItemAsync(key, data).ConfigureAwait(false);
49+
50+
RaiseOnChanged(key, e.OldValue, data);
51+
}
52+
3553
public async ValueTask<T> GetItemAsync<T>(string key)
3654
{
3755
if (string.IsNullOrWhiteSpace(key))
@@ -98,6 +116,24 @@ public void SetItem<T>(string key, T data)
98116
RaiseOnChanged(key, e.OldValue, data);
99117
}
100118

119+
public void SetItemAsString(string key, string data)
120+
{
121+
if (string.IsNullOrWhiteSpace(key))
122+
throw new ArgumentNullException(nameof(key));
123+
124+
if (data is null)
125+
throw new ArgumentNullException(nameof(data));
126+
127+
var e = RaiseOnChangingSync(key, data);
128+
129+
if (e.Cancel)
130+
return;
131+
132+
_storageProvider.SetItem(key, data);
133+
134+
RaiseOnChanged(key, e.OldValue, data);
135+
}
136+
101137
public T GetItem<T>(string key)
102138
{
103139
if (string.IsNullOrWhiteSpace(key))
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
using System;
2+
using System.Text.Json;
3+
using Blazored.SessionStorage.JsonConverters;
4+
using Blazored.SessionStorage.Serialization;
5+
using Blazored.SessionStorage.StorageOptions;
6+
using Blazored.SessionStorage.Testing;
7+
using Microsoft.Extensions.Options;
8+
using Moq;
9+
using Xunit;
10+
11+
namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests
12+
{
13+
public class SetItemAsString
14+
{
15+
private readonly SessionStorageService _sut;
16+
private readonly IStorageProvider _storageProvider;
17+
private readonly IJsonSerializer _serializer;
18+
19+
private const string Key = "testKey";
20+
21+
public SetItemAsString()
22+
{
23+
var mockOptions = new Mock<IOptions<SessionStorageOptions>>();
24+
var jsonOptions = new JsonSerializerOptions();
25+
jsonOptions.Converters.Add(new TimespanJsonConverter());
26+
mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions());
27+
_serializer = new SystemTextJsonSerializer(mockOptions.Object);
28+
_storageProvider = new InMemoryStorageProvider();
29+
_sut = new SessionStorageService(_storageProvider, _serializer);
30+
}
31+
32+
[Theory]
33+
[InlineData("")]
34+
[InlineData(" ")]
35+
[InlineData(null)]
36+
public void ThrowsArgumentNullException_When_KeyIsInvalid(string key)
37+
{
38+
// arrange / act
39+
const string data = "Data";
40+
var action = new Action(() => _sut.SetItemAsString(key, data));
41+
42+
// assert
43+
Assert.Throws<ArgumentNullException>(action);
44+
}
45+
46+
[Fact]
47+
public void ThrowsArgumentNullException_When_DataIsNull()
48+
{
49+
// arrange / act
50+
var data = (string)null;
51+
var action = new Action(() => _sut.SetItemAsString("MyValue", data));
52+
53+
// assert
54+
Assert.Throws<ArgumentNullException>(action);
55+
}
56+
57+
[Fact]
58+
public void RaisesOnChangingEvent_When_SavingNewData()
59+
{
60+
// arrange
61+
var onChangingCalled = false;
62+
_sut.Changing += (_, _) => onChangingCalled = true;
63+
64+
// act
65+
_sut.SetItemAsString("Key", "Data");
66+
67+
// assert
68+
Assert.True(onChangingCalled);
69+
}
70+
71+
[Fact]
72+
public void OnChangingEventContainsEmptyOldValue_When_SavingData()
73+
{
74+
// arrange
75+
var oldValue = "";
76+
_sut.Changing += (_, args) => oldValue = args.OldValue?.ToString();
77+
78+
// act
79+
_sut.SetItemAsString("Key", "Data");
80+
81+
// assert
82+
Assert.Equal(default, oldValue);
83+
}
84+
85+
[Fact]
86+
public void OnChangingEventContainsNewValue_When_SavingNewData()
87+
{
88+
// arrange
89+
const string data = "Data";
90+
var newValue = "";
91+
_sut.Changing += (_, args) => newValue = args.NewValue.ToString();
92+
93+
// act
94+
_sut.SetItemAsString("Key", data);
95+
96+
// assert
97+
Assert.Equal(data, newValue);
98+
}
99+
100+
[Fact]
101+
public void OnChangingEventIsCancelled_When_SettingCancelToTrue_When_SavingNewData()
102+
{
103+
// arrange
104+
_sut.Changing += (_, args) => args.Cancel = true;
105+
106+
// act
107+
_sut.SetItemAsString("Key", "Data");
108+
109+
// assert
110+
Assert.Equal(0, _storageProvider.Length());
111+
}
112+
113+
[Fact]
114+
public void SavesDataToStore()
115+
{
116+
// Act
117+
var valueToSave = "StringValue";
118+
_sut.SetItemAsString(Key, valueToSave);
119+
120+
// Assert
121+
var valueFromStore = _storageProvider.GetItem(Key);
122+
123+
Assert.Equal(1, _storageProvider.Length());
124+
Assert.Equal(valueToSave, valueFromStore);
125+
}
126+
127+
[Fact]
128+
public void OverwriteExistingValueInStore_When_UsingTheSameKey()
129+
{
130+
// Arrange
131+
const string existingValue = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbmlzdHJhdG9yIiwiZXhwIjoxNTg1NjYwNzEyLCJpc3MiOiJDb2RlUmVkQm9va2luZy5TZXJ2ZXIiLCJhdWQiOiJDb2RlUmVkQm9va2luZy5DbGllbnRzIn0.JhK1M1H7NLCFexujJYCDjTn9La0HloGYADMHXGCFksU";
132+
const string newValue = "6QLE0LL7iw7tHPAwold31qUENt3lVTUZxDGqeXQFx38=";
133+
134+
_storageProvider.SetItem(Key, existingValue);
135+
136+
// Act
137+
_sut.SetItemAsString(Key, newValue);
138+
139+
// Assert
140+
var updatedValue = _storageProvider.GetItem(Key);
141+
142+
Assert.Equal(newValue, updatedValue);
143+
}
144+
145+
[Fact]
146+
public void RaisesOnChangedEvent_When_SavingData()
147+
{
148+
// arrange
149+
var onChangedCalled = false;
150+
_sut.Changed += (_, _) => onChangedCalled = true;
151+
152+
// act
153+
_sut.SetItemAsString("Key", "Data");
154+
155+
// assert
156+
Assert.True(onChangedCalled);
157+
}
158+
159+
[Fact]
160+
public void OnChangedEventContainsEmptyOldValue_When_SavingNewData()
161+
{
162+
// arrange
163+
var oldValue = "";
164+
_sut.Changed += (_, args) => oldValue = args.OldValue?.ToString();
165+
166+
// act
167+
_sut.SetItemAsString("Key", "Data");
168+
169+
// assert
170+
Assert.Equal(default, oldValue);
171+
}
172+
173+
[Fact]
174+
public void OnChangedEventContainsNewValue_When_SavingNewData()
175+
{
176+
// arrange
177+
const string data = "Data";
178+
var newValue = "";
179+
_sut.Changed += (_, args) => newValue = args.NewValue.ToString();
180+
181+
// act
182+
_sut.SetItemAsString("Key", data);
183+
184+
// assert
185+
Assert.Equal(data, newValue);
186+
}
187+
188+
[Fact]
189+
public void OnChangedEventContainsOldValue_When_UpdatingExistingData()
190+
{
191+
// arrange
192+
var existingValue = "Foo";
193+
_storageProvider.SetItem("Key", existingValue);
194+
var oldValue = "";
195+
_sut.Changed += (_, args) => oldValue = args.OldValue?.ToString();
196+
197+
// act
198+
_sut.SetItemAsString("Key", "Data");
199+
200+
// assert
201+
Assert.Equal(existingValue, oldValue);
202+
}
203+
}
204+
}

0 commit comments

Comments
 (0)