Skip to content

Commit

Permalink
Fix: Don't create new ContractResolver on every serialization/deseria…
Browse files Browse the repository at this point in the history
…lization (#609)

* Don't create JsonDateTimeContractResolver on every serialization/deserialization.

* Fix some tests for unix systems.

Co-authored-by: Josh Plooster <[email protected]>
  • Loading branch information
jploo and Josh Plooster authored Oct 16, 2020
1 parent 868f44a commit 7809f40
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 20 deletions.
18 changes: 15 additions & 3 deletions src/Akavache.Core/BlobCache/InMemoryBlobCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class InMemoryBlobCache : ISecureBlobCache, IObjectBlobCache, IEnableLogg
private Dictionary<string, CacheEntry> _cache = new Dictionary<string, CacheEntry>();
private bool _disposed;
private DateTimeKind? _dateTimeKind;
private JsonDateTimeContractResolver _jsonDateTimeContractResolver = new JsonDateTimeContractResolver(); // This will make us use ticks instead of json ticks for DateTime.

/// <summary>
/// Initializes a new instance of the <see cref="InMemoryBlobCache"/> class.
Expand Down Expand Up @@ -91,7 +92,16 @@ internal InMemoryBlobCache(
public DateTimeKind? ForcedDateTimeKind
{
get => _dateTimeKind ?? BlobCache.ForcedDateTimeKind;
set => _dateTimeKind = value;

set
{
_dateTimeKind = value;

if (_jsonDateTimeContractResolver != null)
{
_jsonDateTimeContractResolver.ForceDateTimeKindOverride = value;
}
}
}

/// <inheritdoc />
Expand Down Expand Up @@ -442,7 +452,8 @@ protected virtual void Dispose(bool isDisposing)
private byte[] SerializeObject<T>(T value)
{
var settings = Locator.Current.GetService<JsonSerializerSettings>() ?? new JsonSerializerSettings();
settings.ContractResolver = new JsonDateTimeContractResolver(settings.ContractResolver, ForcedDateTimeKind); // This will make us use ticks instead of json ticks for DateTime.
_jsonDateTimeContractResolver.ExistingContractResolver = settings.ContractResolver;
settings.ContractResolver = _jsonDateTimeContractResolver;
var serializer = JsonSerializer.Create(settings);
using (var ms = new MemoryStream())
{
Expand All @@ -457,7 +468,8 @@ private byte[] SerializeObject<T>(T value)
private T DeserializeObject<T>(byte[] data)
{
var settings = Locator.Current.GetService<JsonSerializerSettings>() ?? new JsonSerializerSettings();
settings.ContractResolver = new JsonDateTimeContractResolver(settings.ContractResolver, ForcedDateTimeKind); // This will make us use ticks instead of json ticks for DateTime.
_jsonDateTimeContractResolver.ExistingContractResolver = settings.ContractResolver;
settings.ContractResolver = _jsonDateTimeContractResolver;
var serializer = JsonSerializer.Create(settings);
using (var reader = new BsonDataReader(new MemoryStream(data)))
{
Expand Down
21 changes: 15 additions & 6 deletions src/Akavache.Core/Json/JsonDateTimeContractResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@ namespace Akavache
/// </summary>
internal class JsonDateTimeContractResolver : DefaultContractResolver
{
private readonly IContractResolver _existingContractResolver;
private readonly DateTimeKind? _forceDateTimeKindOverride;
/// <summary>
/// Initializes a new instance of the <see cref="JsonDateTimeContractResolver"/> class.
/// </summary>
public JsonDateTimeContractResolver()
: this(null, null)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="JsonDateTimeContractResolver"/> class.
Expand All @@ -24,14 +29,18 @@ internal class JsonDateTimeContractResolver : DefaultContractResolver
/// <param name="forceDateTimeKindOverride">If we should override the <see cref="DateTimeKind"/>.</param>
public JsonDateTimeContractResolver(IContractResolver contractResolver, DateTimeKind? forceDateTimeKindOverride)
{
_existingContractResolver = contractResolver;
_forceDateTimeKindOverride = forceDateTimeKindOverride;
ExistingContractResolver = contractResolver;
ForceDateTimeKindOverride = forceDateTimeKindOverride;
}

public IContractResolver ExistingContractResolver { get; set; }

public DateTimeKind? ForceDateTimeKindOverride { get; set; }

/// <inheritdoc />
public override JsonContract ResolveContract(Type type)
{
var contract = _existingContractResolver?.ResolveContract(type);
var contract = ExistingContractResolver?.ResolveContract(type);
if (contract?.Converter != null)
{
return contract;
Expand All @@ -44,7 +53,7 @@ public override JsonContract ResolveContract(Type type)

if (type == typeof(DateTime) || type == typeof(DateTime?))
{
contract.Converter = _forceDateTimeKindOverride == DateTimeKind.Local ? JsonDateTimeTickConverter.LocalDateTimeKindDefault : JsonDateTimeTickConverter.Default;
contract.Converter = ForceDateTimeKindOverride == DateTimeKind.Local ? JsonDateTimeTickConverter.LocalDateTimeKindDefault : JsonDateTimeTickConverter.Default;
}
else if (type == typeof(DateTimeOffset) || type == typeof(DateTimeOffset?))
{
Expand Down
18 changes: 15 additions & 3 deletions src/Akavache.Sqlite3/SqlLiteCache/SqlRawPersistentBlobCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class SqlRawPersistentBlobCache : IObjectBlobCache, IEnableLogger, IObjec
private IDisposable _queueThread;
private DateTimeKind? _dateTimeKind;
private bool _disposed;
private JsonDateTimeContractResolver _jsonDateTimeContractResolver = new JsonDateTimeContractResolver(); // This will make us use ticks instead of json ticks for DateTime.

/// <summary>
/// Initializes a new instance of the <see cref="SqlRawPersistentBlobCache"/> class.
Expand All @@ -56,7 +57,16 @@ public SqlRawPersistentBlobCache(string databaseFile, IScheduler scheduler = nul
public DateTimeKind? ForcedDateTimeKind
{
get => _dateTimeKind ?? BlobCache.ForcedDateTimeKind;
set => _dateTimeKind = value;

set
{
_dateTimeKind = value;

if (_jsonDateTimeContractResolver != null)
{
_jsonDateTimeContractResolver.ForceDateTimeKindOverride = value;
}
}
}

/// <inheritdoc />
Expand Down Expand Up @@ -672,7 +682,8 @@ protected virtual IObservable<byte[]> AfterReadFromDiskFilter(byte[] data, ISche
private byte[] SerializeObject<T>(T value)
{
var settings = Locator.Current.GetService<JsonSerializerSettings>() ?? new JsonSerializerSettings();
settings.ContractResolver = new JsonDateTimeContractResolver(settings.ContractResolver, ForcedDateTimeKind); // This will make us use ticks instead of json ticks for DateTime.
_jsonDateTimeContractResolver.ExistingContractResolver = settings.ContractResolver;
settings.ContractResolver = _jsonDateTimeContractResolver;
var serializer = JsonSerializer.Create(settings);
using (var ms = new MemoryStream())
{
Expand All @@ -687,7 +698,8 @@ private byte[] SerializeObject<T>(T value)
private IObservable<T> DeserializeObject<T>(byte[] data)
{
var settings = Locator.Current.GetService<JsonSerializerSettings>() ?? new JsonSerializerSettings();
settings.ContractResolver = new JsonDateTimeContractResolver(settings.ContractResolver, ForcedDateTimeKind); // This will make us use ticks instead of json ticks for DateTime.
_jsonDateTimeContractResolver.ExistingContractResolver = settings.ContractResolver;
settings.ContractResolver = _jsonDateTimeContractResolver;
var serializer = JsonSerializer.Create(settings);
using (var reader = new BsonDataReader(new MemoryStream(data)))
{
Expand Down
7 changes: 1 addition & 6 deletions src/Akavache.Tests/TestBases/DateTimeTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ namespace Akavache.Tests
/// </summary>
public abstract class DateTimeTestBase
{
/// <summary>
/// Time zone for when testing UTC vs local time operations. Just has to be something that doesn't match UTC.
/// </summary>
private const string TestTimeZone = "Pacific Standard Time";

/// <summary>
/// Gets the date time offsets used in theory tests.
/// </summary>
Expand Down Expand Up @@ -56,7 +51,7 @@ public abstract class DateTimeTestBase
/// <summary>
/// Gets the date time when the tests are done to keep them consistent.
/// </summary>
private static DateTime LocalTestNow { get; } = TimeZoneInfo.ConvertTimeFromUtc(TestNow.ToUniversalTime(), TimeZoneInfo.FindSystemTimeZoneById(TestTimeZone));
private static DateTime LocalTestNow { get; } = TimeZoneInfo.ConvertTimeFromUtc(TestNow.ToUniversalTime(), TimeZoneInfo.CreateCustomTimeZone("testTimeZone", TimeSpan.FromHours(6), "Test Time Zone", "Test Time Zone"));

/// <summary>
/// Gets the date time off set when the tests are done to keep them consistent.
Expand Down
40 changes: 38 additions & 2 deletions src/Akavache.Tests/UtilityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.IO;
using System.Linq;
using System.Reactive.Subjects;
using System.Runtime.InteropServices;
using Xunit;

namespace Akavache.Tests
Expand Down Expand Up @@ -36,6 +37,11 @@ public void DirectoryCreateCreatesDirectories()
[Fact]
public void DirectoryCreateThrowsIOExceptionForNonexistentNetworkPaths()
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return;
}

var exception = Assert.Throws<IOException>(() => new DirectoryInfo(@"\\does\not\exist").CreateRecursive());
Assert.StartsWith("The network path was not found", exception.Message);
}
Expand All @@ -46,7 +52,21 @@ public void DirectoryCreateThrowsIOExceptionForNonexistentNetworkPaths()
[Fact]
public void UtilitySplitsAbsolutePaths()
{
Assert.Equal(new[] { @"c:\", "foo", "bar" }, new DirectoryInfo(@"c:\foo\bar").SplitFullPath());
string path;
string expectedRoot;

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
path = @"c:\foo\bar";
expectedRoot = @"c:\";
}
else
{
path = "/foo/bar";
expectedRoot = "/";
}

Assert.Equal(new[] { expectedRoot, "foo", "bar" }, new DirectoryInfo(path).SplitFullPath());
}

/// <summary>
Expand All @@ -55,7 +75,18 @@ public void UtilitySplitsAbsolutePaths()
[Fact]
public void UtilityResolvesAndSplitsRelativePaths()
{
var components = new DirectoryInfo(@"foo\bar").SplitFullPath().ToList();
string path;

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
path = @"foo\bar";
}
else
{
path = "foo/bar";
}

var components = new DirectoryInfo(path).SplitFullPath().ToList();
Assert.True(components.Count > 2);
Assert.Equal(new[] { "foo", "bar" }, components.Skip(components.Count - 2));
}
Expand All @@ -66,6 +97,11 @@ public void UtilityResolvesAndSplitsRelativePaths()
[Fact]
public void UtilitySplitsUncPaths()
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return;
}

Assert.Equal(new[] { @"\\foo\bar", "baz" }, new DirectoryInfo(@"\\foo\bar\baz").SplitFullPath());
}

Expand Down

0 comments on commit 7809f40

Please sign in to comment.