Skip to content

Commit 7809f40

Browse files
jplooJosh Plooster
and
Josh Plooster
authored
Fix: Don't create new ContractResolver on every serialization/deserialization (#609)
* Don't create JsonDateTimeContractResolver on every serialization/deserialization. * Fix some tests for unix systems. Co-authored-by: Josh Plooster <[email protected]>
1 parent 868f44a commit 7809f40

File tree

5 files changed

+84
-20
lines changed

5 files changed

+84
-20
lines changed

src/Akavache.Core/BlobCache/InMemoryBlobCache.cs

+15-3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public class InMemoryBlobCache : ISecureBlobCache, IObjectBlobCache, IEnableLogg
3131
private Dictionary<string, CacheEntry> _cache = new Dictionary<string, CacheEntry>();
3232
private bool _disposed;
3333
private DateTimeKind? _dateTimeKind;
34+
private JsonDateTimeContractResolver _jsonDateTimeContractResolver = new JsonDateTimeContractResolver(); // This will make us use ticks instead of json ticks for DateTime.
3435

3536
/// <summary>
3637
/// Initializes a new instance of the <see cref="InMemoryBlobCache"/> class.
@@ -91,7 +92,16 @@ internal InMemoryBlobCache(
9192
public DateTimeKind? ForcedDateTimeKind
9293
{
9394
get => _dateTimeKind ?? BlobCache.ForcedDateTimeKind;
94-
set => _dateTimeKind = value;
95+
96+
set
97+
{
98+
_dateTimeKind = value;
99+
100+
if (_jsonDateTimeContractResolver != null)
101+
{
102+
_jsonDateTimeContractResolver.ForceDateTimeKindOverride = value;
103+
}
104+
}
95105
}
96106

97107
/// <inheritdoc />
@@ -442,7 +452,8 @@ protected virtual void Dispose(bool isDisposing)
442452
private byte[] SerializeObject<T>(T value)
443453
{
444454
var settings = Locator.Current.GetService<JsonSerializerSettings>() ?? new JsonSerializerSettings();
445-
settings.ContractResolver = new JsonDateTimeContractResolver(settings.ContractResolver, ForcedDateTimeKind); // This will make us use ticks instead of json ticks for DateTime.
455+
_jsonDateTimeContractResolver.ExistingContractResolver = settings.ContractResolver;
456+
settings.ContractResolver = _jsonDateTimeContractResolver;
446457
var serializer = JsonSerializer.Create(settings);
447458
using (var ms = new MemoryStream())
448459
{
@@ -457,7 +468,8 @@ private byte[] SerializeObject<T>(T value)
457468
private T DeserializeObject<T>(byte[] data)
458469
{
459470
var settings = Locator.Current.GetService<JsonSerializerSettings>() ?? new JsonSerializerSettings();
460-
settings.ContractResolver = new JsonDateTimeContractResolver(settings.ContractResolver, ForcedDateTimeKind); // This will make us use ticks instead of json ticks for DateTime.
471+
_jsonDateTimeContractResolver.ExistingContractResolver = settings.ContractResolver;
472+
settings.ContractResolver = _jsonDateTimeContractResolver;
461473
var serializer = JsonSerializer.Create(settings);
462474
using (var reader = new BsonDataReader(new MemoryStream(data)))
463475
{

src/Akavache.Core/Json/JsonDateTimeContractResolver.cs

+15-6
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,13 @@ namespace Akavache
1414
/// </summary>
1515
internal class JsonDateTimeContractResolver : DefaultContractResolver
1616
{
17-
private readonly IContractResolver _existingContractResolver;
18-
private readonly DateTimeKind? _forceDateTimeKindOverride;
17+
/// <summary>
18+
/// Initializes a new instance of the <see cref="JsonDateTimeContractResolver"/> class.
19+
/// </summary>
20+
public JsonDateTimeContractResolver()
21+
: this(null, null)
22+
{
23+
}
1924

2025
/// <summary>
2126
/// Initializes a new instance of the <see cref="JsonDateTimeContractResolver"/> class.
@@ -24,14 +29,18 @@ internal class JsonDateTimeContractResolver : DefaultContractResolver
2429
/// <param name="forceDateTimeKindOverride">If we should override the <see cref="DateTimeKind"/>.</param>
2530
public JsonDateTimeContractResolver(IContractResolver contractResolver, DateTimeKind? forceDateTimeKindOverride)
2631
{
27-
_existingContractResolver = contractResolver;
28-
_forceDateTimeKindOverride = forceDateTimeKindOverride;
32+
ExistingContractResolver = contractResolver;
33+
ForceDateTimeKindOverride = forceDateTimeKindOverride;
2934
}
3035

36+
public IContractResolver ExistingContractResolver { get; set; }
37+
38+
public DateTimeKind? ForceDateTimeKindOverride { get; set; }
39+
3140
/// <inheritdoc />
3241
public override JsonContract ResolveContract(Type type)
3342
{
34-
var contract = _existingContractResolver?.ResolveContract(type);
43+
var contract = ExistingContractResolver?.ResolveContract(type);
3544
if (contract?.Converter != null)
3645
{
3746
return contract;
@@ -44,7 +53,7 @@ public override JsonContract ResolveContract(Type type)
4453

4554
if (type == typeof(DateTime) || type == typeof(DateTime?))
4655
{
47-
contract.Converter = _forceDateTimeKindOverride == DateTimeKind.Local ? JsonDateTimeTickConverter.LocalDateTimeKindDefault : JsonDateTimeTickConverter.Default;
56+
contract.Converter = ForceDateTimeKindOverride == DateTimeKind.Local ? JsonDateTimeTickConverter.LocalDateTimeKindDefault : JsonDateTimeTickConverter.Default;
4857
}
4958
else if (type == typeof(DateTimeOffset) || type == typeof(DateTimeOffset?))
5059
{

src/Akavache.Sqlite3/SqlLiteCache/SqlRawPersistentBlobCache.cs

+15-3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public class SqlRawPersistentBlobCache : IObjectBlobCache, IEnableLogger, IObjec
3636
private IDisposable _queueThread;
3737
private DateTimeKind? _dateTimeKind;
3838
private bool _disposed;
39+
private JsonDateTimeContractResolver _jsonDateTimeContractResolver = new JsonDateTimeContractResolver(); // This will make us use ticks instead of json ticks for DateTime.
3940

4041
/// <summary>
4142
/// Initializes a new instance of the <see cref="SqlRawPersistentBlobCache"/> class.
@@ -56,7 +57,16 @@ public SqlRawPersistentBlobCache(string databaseFile, IScheduler scheduler = nul
5657
public DateTimeKind? ForcedDateTimeKind
5758
{
5859
get => _dateTimeKind ?? BlobCache.ForcedDateTimeKind;
59-
set => _dateTimeKind = value;
60+
61+
set
62+
{
63+
_dateTimeKind = value;
64+
65+
if (_jsonDateTimeContractResolver != null)
66+
{
67+
_jsonDateTimeContractResolver.ForceDateTimeKindOverride = value;
68+
}
69+
}
6070
}
6171

6272
/// <inheritdoc />
@@ -672,7 +682,8 @@ protected virtual IObservable<byte[]> AfterReadFromDiskFilter(byte[] data, ISche
672682
private byte[] SerializeObject<T>(T value)
673683
{
674684
var settings = Locator.Current.GetService<JsonSerializerSettings>() ?? new JsonSerializerSettings();
675-
settings.ContractResolver = new JsonDateTimeContractResolver(settings.ContractResolver, ForcedDateTimeKind); // This will make us use ticks instead of json ticks for DateTime.
685+
_jsonDateTimeContractResolver.ExistingContractResolver = settings.ContractResolver;
686+
settings.ContractResolver = _jsonDateTimeContractResolver;
676687
var serializer = JsonSerializer.Create(settings);
677688
using (var ms = new MemoryStream())
678689
{
@@ -687,7 +698,8 @@ private byte[] SerializeObject<T>(T value)
687698
private IObservable<T> DeserializeObject<T>(byte[] data)
688699
{
689700
var settings = Locator.Current.GetService<JsonSerializerSettings>() ?? new JsonSerializerSettings();
690-
settings.ContractResolver = new JsonDateTimeContractResolver(settings.ContractResolver, ForcedDateTimeKind); // This will make us use ticks instead of json ticks for DateTime.
701+
_jsonDateTimeContractResolver.ExistingContractResolver = settings.ContractResolver;
702+
settings.ContractResolver = _jsonDateTimeContractResolver;
691703
var serializer = JsonSerializer.Create(settings);
692704
using (var reader = new BsonDataReader(new MemoryStream(data)))
693705
{

src/Akavache.Tests/TestBases/DateTimeTestBase.cs

+1-6
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,6 @@ namespace Akavache.Tests
1616
/// </summary>
1717
public abstract class DateTimeTestBase
1818
{
19-
/// <summary>
20-
/// Time zone for when testing UTC vs local time operations. Just has to be something that doesn't match UTC.
21-
/// </summary>
22-
private const string TestTimeZone = "Pacific Standard Time";
23-
2419
/// <summary>
2520
/// Gets the date time offsets used in theory tests.
2621
/// </summary>
@@ -56,7 +51,7 @@ public abstract class DateTimeTestBase
5651
/// <summary>
5752
/// Gets the date time when the tests are done to keep them consistent.
5853
/// </summary>
59-
private static DateTime LocalTestNow { get; } = TimeZoneInfo.ConvertTimeFromUtc(TestNow.ToUniversalTime(), TimeZoneInfo.FindSystemTimeZoneById(TestTimeZone));
54+
private static DateTime LocalTestNow { get; } = TimeZoneInfo.ConvertTimeFromUtc(TestNow.ToUniversalTime(), TimeZoneInfo.CreateCustomTimeZone("testTimeZone", TimeSpan.FromHours(6), "Test Time Zone", "Test Time Zone"));
6055

6156
/// <summary>
6257
/// Gets the date time off set when the tests are done to keep them consistent.

src/Akavache.Tests/UtilityTests.cs

+38-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.IO;
88
using System.Linq;
99
using System.Reactive.Subjects;
10+
using System.Runtime.InteropServices;
1011
using Xunit;
1112

1213
namespace Akavache.Tests
@@ -36,6 +37,11 @@ public void DirectoryCreateCreatesDirectories()
3637
[Fact]
3738
public void DirectoryCreateThrowsIOExceptionForNonexistentNetworkPaths()
3839
{
40+
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
41+
{
42+
return;
43+
}
44+
3945
var exception = Assert.Throws<IOException>(() => new DirectoryInfo(@"\\does\not\exist").CreateRecursive());
4046
Assert.StartsWith("The network path was not found", exception.Message);
4147
}
@@ -46,7 +52,21 @@ public void DirectoryCreateThrowsIOExceptionForNonexistentNetworkPaths()
4652
[Fact]
4753
public void UtilitySplitsAbsolutePaths()
4854
{
49-
Assert.Equal(new[] { @"c:\", "foo", "bar" }, new DirectoryInfo(@"c:\foo\bar").SplitFullPath());
55+
string path;
56+
string expectedRoot;
57+
58+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
59+
{
60+
path = @"c:\foo\bar";
61+
expectedRoot = @"c:\";
62+
}
63+
else
64+
{
65+
path = "/foo/bar";
66+
expectedRoot = "/";
67+
}
68+
69+
Assert.Equal(new[] { expectedRoot, "foo", "bar" }, new DirectoryInfo(path).SplitFullPath());
5070
}
5171

5272
/// <summary>
@@ -55,7 +75,18 @@ public void UtilitySplitsAbsolutePaths()
5575
[Fact]
5676
public void UtilityResolvesAndSplitsRelativePaths()
5777
{
58-
var components = new DirectoryInfo(@"foo\bar").SplitFullPath().ToList();
78+
string path;
79+
80+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
81+
{
82+
path = @"foo\bar";
83+
}
84+
else
85+
{
86+
path = "foo/bar";
87+
}
88+
89+
var components = new DirectoryInfo(path).SplitFullPath().ToList();
5990
Assert.True(components.Count > 2);
6091
Assert.Equal(new[] { "foo", "bar" }, components.Skip(components.Count - 2));
6192
}
@@ -66,6 +97,11 @@ public void UtilityResolvesAndSplitsRelativePaths()
6697
[Fact]
6798
public void UtilitySplitsUncPaths()
6899
{
100+
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
101+
{
102+
return;
103+
}
104+
69105
Assert.Equal(new[] { @"\\foo\bar", "baz" }, new DirectoryInfo(@"\\foo\bar\baz").SplitFullPath());
70106
}
71107

0 commit comments

Comments
 (0)