Skip to content

Commit

Permalink
Allow specifying a custom JsonSerializerOptions, Close #241
Browse files Browse the repository at this point in the history
  • Loading branch information
VahidN committed May 24, 2024
1 parent f6dfc68 commit 399bba4
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 41 deletions.
1 change: 1 addition & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ dotnet_diagnostic.S3878.severity = suggestion
dotnet_diagnostic.S127.severity = suggestion
dotnet_diagnostic.S2857.severity = suggestion
dotnet_diagnostic.S2094.severity = suggestion
dotnet_diagnostic.IDE0055.severity = none

# CA1510: Use 'ArgumentNullException.ThrowIfNull' instead of explicitly throwing a new exception instance
dotnet_diagnostic.CA1510.severity = suggestion
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,11 @@ namespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample
If you want to disable this interceptor for a while, use the `.EnableCachingInterceptor(enable: false)` method. Its default value is true.


## Providing options to control the serialization behavior

The EFCacheKeyProvider class serializes parameter values of a DbCommand to JSON values. If it causes an exception in some cases, you can specify a custom JsonSerializerOptions for it using the `UseJsonSerializerOptions(options)` method.


## Does it work?!

You should enable the logging system to see the behind the scene of the caching interceptor.
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "8.0.204",
"version": "8.0.300",
"rollForward": "latestMajor",
"allowPrerelease": true
}
Expand Down
106 changes: 70 additions & 36 deletions src/EFCoreSecondLevelCacheInterceptor/EFCacheKeyProvider.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
using System;
using System.Collections;
using System.Data.Common;
using System.Globalization;
using System.Text;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
#if NET5_0 || NET6_0 || NET7_0 || NET8_0
using System.Text.Json;

#else
using System.Collections;
using System.Globalization;
#endif

namespace EFCoreSecondLevelCacheInterceptor;

Expand All @@ -13,6 +19,10 @@ namespace EFCoreSecondLevelCacheInterceptor;
/// </summary>
public class EFCacheKeyProvider : IEFCacheKeyProvider
{
#if NET5_0 || NET6_0 || NET7_0 || NET8_0
private readonly EFCoreSecondLevelCacheSettings _settings;
#endif

private readonly IEFCacheDependenciesProcessor _cacheDependenciesProcessor;
private readonly IEFCacheKeyPrefixProvider _cacheKeyPrefixProvider;
private readonly IEFCachePolicyParser _cachePolicyParser;
Expand All @@ -25,18 +35,26 @@ public class EFCacheKeyProvider : IEFCacheKeyProvider
/// A custom cache key provider for EF queries.
/// </summary>
public EFCacheKeyProvider(IEFCacheDependenciesProcessor cacheDependenciesProcessor,
IEFCachePolicyParser cachePolicyParser,
IEFDebugLogger logger,
ILogger<EFCacheKeyProvider> keyProviderLogger,
IEFHashProvider hashProvider,
IEFCacheKeyPrefixProvider cacheKeyPrefixProvider)
IEFCachePolicyParser cachePolicyParser,
IEFDebugLogger logger,
ILogger<EFCacheKeyProvider> keyProviderLogger,
IEFHashProvider hashProvider,
IEFCacheKeyPrefixProvider cacheKeyPrefixProvider
#if NET5_0 || NET6_0 || NET7_0 || NET8_0
,
IOptions<EFCoreSecondLevelCacheSettings> cacheSettings
#endif
)
{
_cacheDependenciesProcessor = cacheDependenciesProcessor;
_logger = logger;
_keyProviderLogger = keyProviderLogger;
_cachePolicyParser = cachePolicyParser;
_hashProvider = hashProvider ?? throw new ArgumentNullException(nameof(hashProvider));
_cacheKeyPrefixProvider = cacheKeyPrefixProvider;
#if NET5_0 || NET6_0 || NET7_0 || NET8_0
_settings = cacheSettings?.Value ?? throw new ArgumentNullException(nameof(cacheSettings));
#endif
}

/// <summary>
Expand Down Expand Up @@ -65,27 +83,26 @@ public EFCacheKey GetEFCacheKey(DbCommand command, DbContext context, EFCachePol

var cacheKey = getCacheKey(command, cachePolicy.CacheSaltKey);
var cacheKeyPrefix = _cacheKeyPrefixProvider.GetCacheKeyPrefix();
var cacheKeyHash =
!string.IsNullOrEmpty(cacheKeyPrefix)
? $"{cacheKeyPrefix}{_hashProvider.ComputeHash(cacheKey):X}"
: $"{_hashProvider.ComputeHash(cacheKey):X}";

var cacheKeyHash = !string.IsNullOrEmpty(cacheKeyPrefix)
? $"{cacheKeyPrefix}{_hashProvider.ComputeHash(cacheKey):X}"
: $"{_hashProvider.ComputeHash(cacheKey):X}";

var cacheDbContextType = context.GetType();
var cacheDependencies = _cacheDependenciesProcessor.GetCacheDependencies(command, context, cachePolicy);

if (_logger.IsLoggerEnabled)
{
_keyProviderLogger
.LogDebug("KeyHash: {CacheKeyHash}, DbContext: {Name}, CacheDependencies: {Dependencies}.",
cacheKeyHash,
cacheDbContextType?.Name,
string.Join(", ", cacheDependencies));
_keyProviderLogger.LogDebug(
"KeyHash: {CacheKeyHash}, DbContext: {Name}, CacheDependencies: {Dependencies}.", cacheKeyHash,
cacheDbContextType?.Name, string.Join(", ", cacheDependencies));
}

return new EFCacheKey(cacheDependencies)
{
KeyHash = cacheKeyHash,
DbContext = cacheDbContextType,
};
{
KeyHash = cacheKeyHash,
DbContext = cacheDbContextType
};
}

private string getCacheKey(DbCommand command, string saltKey)
Expand All @@ -103,35 +120,52 @@ private string getCacheKey(DbCommand command, string saltKey)
}

cacheKey.Append(parameter.ParameterName)
.Append('=').Append(getParameterValue(parameter)).Append(',')
.Append("Size").Append('=').Append(parameter.Size).Append(',')
.Append("Precision").Append('=').Append(parameter.Precision).Append(',')
.Append("Scale").Append('=').Append(parameter.Scale).Append(',')
.Append("Direction").Append('=').Append(parameter.Direction).Append(',');
.Append('=')
.Append(GetParameterValue(parameter))
.Append(',')
.Append("Size")
.Append('=')
.Append(parameter.Size)
.Append(',')
.Append("Precision")
.Append('=')
.Append(parameter.Precision)
.Append(',')
.Append("Scale")
.Append('=')
.Append(parameter.Scale)
.Append(',')
.Append("Direction")
.Append('=')
.Append(parameter.Direction)
.Append(',');
}

cacheKey.AppendLine("SaltKey").Append('=').Append(saltKey);

return cacheKey.ToString().Trim();
}

#if NET5_0 || NET6_0 || NET7_0 || NET8_0
private static string? getParameterValue(DbParameter parameter)
=> System.Text.Json.JsonSerializer.Serialize(parameter.Value);
#else
private static string? getParameterValue(DbParameter parameter)
#if NET5_0 || NET6_0 || NET7_0 || NET8_0
private string? GetParameterValue(DbParameter parameter)
=> _settings.JsonSerializerOptions is null
? JsonSerializer.Serialize(parameter.Value)
: JsonSerializer.Serialize(parameter.Value, _settings.JsonSerializerOptions);
#else
private static string? GetParameterValue(DbParameter parameter)
{
return parameter.Value switch
{
DBNull => "null",
null => "null",
byte[] buffer => bytesToHex(buffer),
Array array => enumerableToString(array),
IEnumerable enumerable => enumerableToString(enumerable),
byte[] buffer => BytesToHex(buffer),
Array array => EnumerableToString(array),
IEnumerable enumerable => EnumerableToString(enumerable),
_ => Convert.ToString(parameter.Value, CultureInfo.InvariantCulture),
};
}

private static string enumerableToString(IEnumerable array)
private static string EnumerableToString(IEnumerable array)
{
var sb = new StringBuilder();
foreach (var item in array)
Expand All @@ -142,7 +176,7 @@ private static string enumerableToString(IEnumerable array)
return sb.ToString();
}

private static string bytesToHex(byte[] buffer)
private static string BytesToHex(byte[] buffer)
{
var sb = new StringBuilder(buffer.Length * 2);
foreach (var @byte in buffer)
Expand All @@ -152,5 +186,5 @@ private static string bytesToHex(byte[] buffer)

return sb.ToString();
}
#endif
#endif
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Entity Framework Core Second Level Caching Library.</Description>
<VersionPrefix>4.4.3</VersionPrefix>
<VersionPrefix>4.5.0</VersionPrefix>
<Authors>Vahid Nasiri</Authors>
<TargetFrameworks>net8.0;net7.0;net6.0;net5.0;netstandard2.1;netstandard2.0;net462;netcoreapp3.1;</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
Expand Down Expand Up @@ -40,11 +40,11 @@
<ReportAnalyzer>true</ReportAnalyzer>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Meziantou.Analyzer" Version="2.0.149">
<PackageReference Include="Meziantou.Analyzer" Version="2.0.153">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.9.28">
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.10.48">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand All @@ -56,7 +56,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.23.2.88755">
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.25.1.91650">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System;
#if NET5_0 || NET6_0 || NET7_0 || NET8_0
using System.Text.Json;
#endif

namespace EFCoreSecondLevelCacheInterceptor;

Expand All @@ -9,6 +12,20 @@ public class EFCoreSecondLevelCacheOptions
{
internal EFCoreSecondLevelCacheSettings Settings { get; } = new();

#if NET5_0 || NET6_0 || NET7_0 || NET8_0
/// <summary>
/// Provides options to control the serialization behavior.
/// EFCacheKeyProvider uses these options to serialize the parameter values.
/// Its default value is null.
/// </summary>
public EFCoreSecondLevelCacheOptions UseJsonSerializerOptions(JsonSerializerOptions? options)
{
Settings.JsonSerializerOptions = options;

return this;
}
#endif

/// <summary>
/// Puts the whole system in cache. In this case calling the `Cacheable()` methods won't be necessary.
/// If you specify the `Cacheable()` method, its setting will override this global setting.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System;
#if NET5_0 || NET6_0 || NET7_0 || NET8_0
using System.Text.Json;
#endif

namespace EFCoreSecondLevelCacheInterceptor;

Expand All @@ -7,6 +10,13 @@ namespace EFCoreSecondLevelCacheInterceptor;
/// </summary>
public class EFCoreSecondLevelCacheSettings
{
#if NET5_0 || NET6_0 || NET7_0 || NET8_0
/// <summary>
/// Options to control the serialization behavior
/// </summary>
public JsonSerializerOptions? JsonSerializerOptions { get; set; }
#endif

/// <summary>
/// The selected cache provider
/// </summary>
Expand Down

0 comments on commit 399bba4

Please sign in to comment.