From 90123e64ad9de946792cc950768a009da78dc6ea Mon Sep 17 00:00:00 2001 From: Jacob Atchley Date: Mon, 8 Jul 2024 17:08:34 -0500 Subject: [PATCH 1/4] feat: allow for specifying custom json serialization --- .../LitRedisServiceCollectionBuilder.cs | 10 ++++ ...ltLitRedisSystemTextJsonOptionsProvider.cs | 10 ++++ .../Implementations/LitRedisCacheStore.cs | 13 +++-- .../LitRedisSystemTextJsonSerializer.cs | 18 +++++++ .../Interfaces/ILitRedisJsonSerializer.cs | 8 ++++ .../ILitRedisSystemTextJsonOptionsProvider.cs | 8 ++++ LitRedis.Core/LitRedis.Core.csproj | 2 +- LitRedis.Core/ServiceProviderExtensions.cs | 3 +- LitRedis.Sample/Program.cs | 5 +- .../Implementations/JsonSerializationTests.cs | 47 +++++++++++++++++++ .../LitRedisCacheStoreTests.cs | 2 + 11 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 LitRedis.Core/Implementations/DefaultLitRedisSystemTextJsonOptionsProvider.cs create mode 100644 LitRedis.Core/Implementations/LitRedisSystemTextJsonSerializer.cs create mode 100644 LitRedis.Core/Interfaces/ILitRedisJsonSerializer.cs create mode 100644 LitRedis.Core/Interfaces/ILitRedisSystemTextJsonOptionsProvider.cs create mode 100644 LitRedis.Tests/Core/Implementations/JsonSerializationTests.cs diff --git a/LitRedis.Core/Builders/LitRedisServiceCollectionBuilder.cs b/LitRedis.Core/Builders/LitRedisServiceCollectionBuilder.cs index 198352f..038ac8f 100644 --- a/LitRedis.Core/Builders/LitRedisServiceCollectionBuilder.cs +++ b/LitRedis.Core/Builders/LitRedisServiceCollectionBuilder.cs @@ -1,4 +1,5 @@ using System; +using System.Text.Json; using LitRedis.Core.Implementations; using LitRedis.Core.Interfaces; using LitRedis.Core.Models; @@ -11,11 +12,14 @@ public class LitRedisServiceCollectionBuilder { public IServiceCollection ServiceCollection { get; } private readonly LitRedisOptions _litRedisOptions = new(); + private readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web); public LitRedisServiceCollectionBuilder(IServiceCollection serviceCollection) { ServiceCollection = serviceCollection; + serviceCollection.TryAddSingleton(_litRedisOptions); + serviceCollection.TryAddSingleton(new DefaultLitRedisSystemTextJsonOptionsProvider(_jsonOptions)); serviceCollection.TryAddSingleton(); serviceCollection.TryAddScoped(); } @@ -43,4 +47,10 @@ public LitRedisServiceCollectionBuilder WithLitRedisOptions(Action configure) + { + configure(_jsonOptions); + return this; + } } diff --git a/LitRedis.Core/Implementations/DefaultLitRedisSystemTextJsonOptionsProvider.cs b/LitRedis.Core/Implementations/DefaultLitRedisSystemTextJsonOptionsProvider.cs new file mode 100644 index 0000000..5125852 --- /dev/null +++ b/LitRedis.Core/Implementations/DefaultLitRedisSystemTextJsonOptionsProvider.cs @@ -0,0 +1,10 @@ +using System.Text.Json; +using LitRedis.Core.Interfaces; + +namespace LitRedis.Core.Implementations; + +public class DefaultLitRedisSystemTextJsonOptionsProvider(JsonSerializerOptions options) + : ILitRedisSystemTextJsonOptionsProvider +{ + public JsonSerializerOptions GetOptions() => options; +} diff --git a/LitRedis.Core/Implementations/LitRedisCacheStore.cs b/LitRedis.Core/Implementations/LitRedisCacheStore.cs index a713de5..8673be0 100644 --- a/LitRedis.Core/Implementations/LitRedisCacheStore.cs +++ b/LitRedis.Core/Implementations/LitRedisCacheStore.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text.Json; using System.Threading; using System.Threading.Tasks; using LitRedis.Core.Interfaces; @@ -14,11 +13,15 @@ public class LitRedisCacheStore : ILitRedisCacheStore { private readonly IMemoryCache _cache; private readonly ILitRedisConnectionService _litRedisConnectionService; + private readonly ILitRedisJsonSerializer _jsonSerializer; - public LitRedisCacheStore(IMemoryCache cache, ILitRedisConnectionService litRedisConnectionService) + public LitRedisCacheStore(IMemoryCache cache, + ILitRedisConnectionService litRedisConnectionService, + ILitRedisJsonSerializer jsonSerializer) { _cache = cache; _litRedisConnectionService = litRedisConnectionService; + _jsonSerializer = jsonSerializer; } private static void KeyGuard(string key) @@ -43,9 +46,9 @@ public async Task PutAsync(string key, T model, TimeSpan? expiry, Cancellatio return; } - var str = JsonSerializer.Serialize(model); + var json = _jsonSerializer.Serialize(model); - await _litRedisConnectionService.UseDbAsync((db, _) => db.StringSetAsync(key, str, expiry), cancellationToken); + await _litRedisConnectionService.UseDbAsync((db, _) => db.StringSetAsync(key, json, expiry), cancellationToken); } /// @@ -57,7 +60,7 @@ public async Task GetAsync(string key, CancellationToken cancellationToken var str = await GetAsync(key, cancellationToken); - return string.IsNullOrWhiteSpace(str) ? default : JsonSerializer.Deserialize(str); + return string.IsNullOrWhiteSpace(str) ? default : _jsonSerializer.Deserialize(str); } /// diff --git a/LitRedis.Core/Implementations/LitRedisSystemTextJsonSerializer.cs b/LitRedis.Core/Implementations/LitRedisSystemTextJsonSerializer.cs new file mode 100644 index 0000000..60dbd7b --- /dev/null +++ b/LitRedis.Core/Implementations/LitRedisSystemTextJsonSerializer.cs @@ -0,0 +1,18 @@ +using System.Text.Json; +using LitRedis.Core.Interfaces; + +namespace LitRedis.Core.Implementations; + +public class LitRedisSystemTextJsonSerializer : ILitRedisJsonSerializer +{ + private readonly ILitRedisSystemTextJsonOptionsProvider _optionsProvider; + + public LitRedisSystemTextJsonSerializer(ILitRedisSystemTextJsonOptionsProvider optionsProvider) + { + _optionsProvider = optionsProvider; + } + + public string Serialize(T value) => JsonSerializer.Serialize(value, _optionsProvider.GetOptions()); + + public T Deserialize(string value) => JsonSerializer.Deserialize(value, _optionsProvider.GetOptions()); +} diff --git a/LitRedis.Core/Interfaces/ILitRedisJsonSerializer.cs b/LitRedis.Core/Interfaces/ILitRedisJsonSerializer.cs new file mode 100644 index 0000000..ff37d22 --- /dev/null +++ b/LitRedis.Core/Interfaces/ILitRedisJsonSerializer.cs @@ -0,0 +1,8 @@ +namespace LitRedis.Core.Interfaces; + +public interface ILitRedisJsonSerializer +{ + string Serialize(T value); + + T Deserialize(string value); +} diff --git a/LitRedis.Core/Interfaces/ILitRedisSystemTextJsonOptionsProvider.cs b/LitRedis.Core/Interfaces/ILitRedisSystemTextJsonOptionsProvider.cs new file mode 100644 index 0000000..27dbfeb --- /dev/null +++ b/LitRedis.Core/Interfaces/ILitRedisSystemTextJsonOptionsProvider.cs @@ -0,0 +1,8 @@ +using System.Text.Json; + +namespace LitRedis.Core.Interfaces; + +public interface ILitRedisSystemTextJsonOptionsProvider +{ + JsonSerializerOptions GetOptions(); +} diff --git a/LitRedis.Core/LitRedis.Core.csproj b/LitRedis.Core/LitRedis.Core.csproj index 694476f..8929ae3 100644 --- a/LitRedis.Core/LitRedis.Core.csproj +++ b/LitRedis.Core/LitRedis.Core.csproj @@ -2,7 +2,7 @@ netstandard2.1 - 4.0.3 + 4.0.4 latestmajor diff --git a/LitRedis.Core/ServiceProviderExtensions.cs b/LitRedis.Core/ServiceProviderExtensions.cs index 87b4bf7..f535483 100644 --- a/LitRedis.Core/ServiceProviderExtensions.cs +++ b/LitRedis.Core/ServiceProviderExtensions.cs @@ -7,7 +7,8 @@ namespace LitRedis.Core; //todo: test public static class ServiceProviderExtensions { - public static IServiceCollection AddLitRedis(this IServiceCollection serviceCollection, + public static IServiceCollection AddLitRedis( + this IServiceCollection serviceCollection, Action configure) { var builder = new LitRedisServiceCollectionBuilder(serviceCollection); diff --git a/LitRedis.Sample/Program.cs b/LitRedis.Sample/Program.cs index e09044c..5f78d1a 100644 --- a/LitRedis.Sample/Program.cs +++ b/LitRedis.Sample/Program.cs @@ -23,7 +23,10 @@ private static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefau .ConfigureServices((_, services) => { services - .AddLitRedis(redis => redis.WithCaching().WithLocking().WithConnectionString("localhost:6379,defaultDatabase=0")) + .AddLitRedis(redis => redis + .WithCaching() + .WithLocking() + .WithConnectionString("localhost:6379,defaultDatabase=0")) .AddHostedService() .AddLogging(o => o.AddSimpleConsole(c => c.TimestampFormat = "[yyy-MM-dd HH:mm:ss] ")); }); diff --git a/LitRedis.Tests/Core/Implementations/JsonSerializationTests.cs b/LitRedis.Tests/Core/Implementations/JsonSerializationTests.cs new file mode 100644 index 0000000..7d37c76 --- /dev/null +++ b/LitRedis.Tests/Core/Implementations/JsonSerializationTests.cs @@ -0,0 +1,47 @@ +using System.Drawing; +using System.Dynamic; +using System.Text.Json; +using FluentAssertions; +using LitRedis.Core.Builders; +using LitRedis.Core.Interfaces; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace LitRedis.Tests.Core.Implementations; + +class SampleClass +{ + public string Name { get; set; } + public int Age { get; set; } + public ExpandoObject Custom { get; set; } +} + +[TestClass] +public class JsonSerializationTests +{ + [TestMethod] + public void Json_Serializer_Should_Handle_Expando() + { + dynamic custom = new ExpandoObject(); + custom.Color = "Red"; + custom.Size = "Large"; + + var sample = new SampleClass + { + Name = "John Doe", + Age = 30, + Custom = custom + }; + + var sp = new LitRedisServiceCollectionBuilder(new ServiceCollection()).ServiceCollection.BuildServiceProvider(); + var options = sp.GetRequiredService().GetOptions(); + var serialized = JsonSerializer.Serialize(sample, options); + var deserialized = JsonSerializer.Deserialize(serialized, options); + var serializedAgain = JsonSerializer.Serialize(deserialized, options); + serializedAgain.Should() + .Be("{\"name\":\"John Doe\",\"age\":30,\"custom\":{\"Color\":\"Red\",\"Size\":\"Large\"}}"); + dynamic c = deserialized.Custom; + string color = c.Color.ToString(); + color.Should().Be("Red"); + } +} diff --git a/LitRedis.Tests/Core/Implementations/LitRedisCacheStoreTests.cs b/LitRedis.Tests/Core/Implementations/LitRedisCacheStoreTests.cs index 76a5bb7..c768ca4 100644 --- a/LitRedis.Tests/Core/Implementations/LitRedisCacheStoreTests.cs +++ b/LitRedis.Tests/Core/Implementations/LitRedisCacheStoreTests.cs @@ -57,6 +57,8 @@ public async Task Lit_Redis_Cache_Store_Should_Get_Value_Object() var mockRedisConnectionService = fixture.Freeze>(); + fixture.Inject(new LitRedisSystemTextJsonSerializer(new DefaultLitRedisSystemTextJsonOptionsProvider(new JsonSerializerOptions(JsonSerializerDefaults.Web)))); + mockRedisConnectionService.Setup(x => x.UseDbAsync(It.IsAny>>(), It.IsAny())) .ReturnsAsync(new RedisValue(JsonSerializer.Serialize(cacheValue))); From 2ceee04f8aa24477e522ba175ca8f2650ae89549 Mon Sep 17 00:00:00 2001 From: Jacob Atchley Date: Mon, 8 Jul 2024 17:26:43 -0500 Subject: [PATCH 2/4] fix: ioc issue --- LitRedis.Core/Builders/LitRedisServiceCollectionBuilder.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/LitRedis.Core/Builders/LitRedisServiceCollectionBuilder.cs b/LitRedis.Core/Builders/LitRedisServiceCollectionBuilder.cs index 038ac8f..c1e5f35 100644 --- a/LitRedis.Core/Builders/LitRedisServiceCollectionBuilder.cs +++ b/LitRedis.Core/Builders/LitRedisServiceCollectionBuilder.cs @@ -19,6 +19,7 @@ public LitRedisServiceCollectionBuilder(IServiceCollection serviceCollection) ServiceCollection = serviceCollection; serviceCollection.TryAddSingleton(_litRedisOptions); + serviceCollection.TryAddSingleton(); serviceCollection.TryAddSingleton(new DefaultLitRedisSystemTextJsonOptionsProvider(_jsonOptions)); serviceCollection.TryAddSingleton(); serviceCollection.TryAddScoped(); From 7ac2a465cd11edaf62ea48aa47fe27d3e912e66f Mon Sep 17 00:00:00 2001 From: Jacob Atchley Date: Mon, 8 Jul 2024 17:28:49 -0500 Subject: [PATCH 3/4] feat: method to replace serializer --- LitRedis.Core/Builders/LitRedisServiceCollectionBuilder.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/LitRedis.Core/Builders/LitRedisServiceCollectionBuilder.cs b/LitRedis.Core/Builders/LitRedisServiceCollectionBuilder.cs index c1e5f35..28a6afc 100644 --- a/LitRedis.Core/Builders/LitRedisServiceCollectionBuilder.cs +++ b/LitRedis.Core/Builders/LitRedisServiceCollectionBuilder.cs @@ -49,6 +49,13 @@ public LitRedisServiceCollectionBuilder WithLitRedisOptions(Action() where T : class, ILitRedisJsonSerializer + { + ServiceCollection.RemoveAll(); + ServiceCollection.TryAddSingleton(); + return this; + } + public LitRedisServiceCollectionBuilder WithJsonOptions(Action configure) { configure(_jsonOptions); From a055462072bb9f8df4bd3d9d6de405477a0ea5e4 Mon Sep 17 00:00:00 2001 From: Jacob Atchley Date: Mon, 8 Jul 2024 17:30:42 -0500 Subject: [PATCH 4/4] fix: format --- LitRedis.Tests/Core/Implementations/JsonSerializationTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LitRedis.Tests/Core/Implementations/JsonSerializationTests.cs b/LitRedis.Tests/Core/Implementations/JsonSerializationTests.cs index 7d37c76..ba0f19c 100644 --- a/LitRedis.Tests/Core/Implementations/JsonSerializationTests.cs +++ b/LitRedis.Tests/Core/Implementations/JsonSerializationTests.cs @@ -9,7 +9,7 @@ namespace LitRedis.Tests.Core.Implementations; -class SampleClass +internal class SampleClass { public string Name { get; set; } public int Age { get; set; }