Skip to content

Commit c5a2ac9

Browse files
committed
Add more formatters to EFMessagePackSerializer
1 parent 39eade8 commit c5a2ac9

File tree

13 files changed

+246
-97
lines changed

13 files changed

+246
-97
lines changed

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project>
22
<PropertyGroup>
3-
<VersionPrefix>5.3.0</VersionPrefix>
3+
<VersionPrefix>5.3.1</VersionPrefix>
44
<LangVersion>latest</LangVersion>
55
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
66
<AccelerateBuildsInVisualStudio>true</AccelerateBuildsInVisualStudio>

src/EFCoreSecondLevelCacheInterceptor.StackExchange.Redis/EFMessagePackSerializer.cs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using MessagePack;
2+
using MessagePack.Formatters;
23
using MessagePack.Resolvers;
34
using Microsoft.Extensions.Options;
45

@@ -10,11 +11,15 @@ namespace EFCoreSecondLevelCacheInterceptor;
1011
public class EFMessagePackSerializer : IEFDataSerializer
1112
{
1213
private static readonly IFormatterResolver CustomResolvers = CompositeResolver.Create(
13-
[EFMessagePackDBNullFormatter.Instance],
1414
[
15-
NativeDateTimeResolver.Instance, ContractlessStandardResolver.Instance,
16-
StandardResolverAllowPrivate.Instance, TypelessContractlessStandardResolver.Instance,
17-
DynamicGenericResolver.Instance
15+
EFMessagePackDBNullFormatter.Instance, NativeDateTimeArrayFormatter.Instance,
16+
NativeDateTimeFormatter.Instance,
17+
NativeDecimalFormatter.Instance, NativeGuidFormatter.Instance, TypelessFormatter.Instance
18+
],
19+
[
20+
NativeDateTimeResolver.Instance, NativeDecimalResolver.Instance, NativeGuidResolver.Instance,
21+
ContractlessStandardResolver.Instance, StandardResolverAllowPrivate.Instance,
22+
TypelessContractlessStandardResolver.Instance, DynamicGenericResolver.Instance
1823
]);
1924

2025
private readonly bool _enableCompression;
@@ -32,7 +37,7 @@ public EFMessagePackSerializer(IOptions<EFCoreSecondLevelCacheSettings> cacheSet
3237
_enableCompression = options.EnableCompression;
3338
}
3439

35-
private MessagePackSerializerOptions MessagePackSerializerOptions
40+
private MessagePackSerializerOptions EfMessagePackSerializerOptions
3641
=> _messagePackSerializerOptions ??= GetSerializerOptions();
3742

3843
/// <summary>
@@ -41,7 +46,7 @@ private MessagePackSerializerOptions MessagePackSerializerOptions
4146
/// <param name="obj"></param>
4247
/// <typeparam name="T"></typeparam>
4348
/// <returns></returns>
44-
public byte[] Serialize<T>(T? obj) => MessagePackSerializer.Serialize(obj, MessagePackSerializerOptions);
49+
public byte[] Serialize<T>(T? obj) => MessagePackSerializer.Serialize(obj, EfMessagePackSerializerOptions);
4550

4651
/// <summary>
4752
/// Deserializes a value of a given type from a sequence of bytes.
@@ -50,7 +55,7 @@ private MessagePackSerializerOptions MessagePackSerializerOptions
5055
/// <typeparam name="T"></typeparam>
5156
/// <returns></returns>
5257
public T? Deserialize<T>(byte[]? data)
53-
=> data is null ? default : MessagePackSerializer.Deserialize<T>(data, MessagePackSerializerOptions);
58+
=> data is null ? default : MessagePackSerializer.Deserialize<T>(data, EfMessagePackSerializerOptions);
5459

5560
private MessagePackSerializerOptions GetSerializerOptions()
5661
=> _enableCompression

src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFCoreSecondLevelCacheInterceptor.UnitTests.csproj

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@
1414
</PropertyGroup>
1515

1616
<ItemGroup>
17-
<PackageReference Include="coverlet.collector" />
18-
<PackageReference Include="Microsoft.NET.Test.Sdk" />
19-
<PackageReference Include="Moq" />
20-
<PackageReference Include="xunit" />
21-
<PackageReference Include="xunit.runner.visualstudio" />
17+
<PackageReference Include="coverlet.collector"/>
18+
<PackageReference Include="Microsoft.NET.Test.Sdk"/>
19+
<PackageReference Include="Moq"/>
20+
<PackageReference Include="MSTest.TestFramework"/>
21+
<PackageReference Include="xunit"/>
22+
<PackageReference Include="xunit.runner.visualstudio"/>
2223
</ItemGroup>
2324

2425
<ItemGroup>
@@ -27,6 +28,7 @@
2728

2829
<ItemGroup>
2930
<ProjectReference Include="..\..\EFCoreSecondLevelCacheInterceptor.MemoryCache\EFCoreSecondLevelCacheInterceptor.MemoryCache.csproj"/>
31+
<ProjectReference Include="..\..\EFCoreSecondLevelCacheInterceptor.StackExchange.Redis\EFCoreSecondLevelCacheInterceptor.StackExchange.Redis.csproj"/>
3032
<ProjectReference Include="..\..\EFCoreSecondLevelCacheInterceptor\EFCoreSecondLevelCacheInterceptor.csproj"/>
3133
</ItemGroup>
3234
</Project>
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using Microsoft.Extensions.Options;
2+
using Moq;
3+
using Assert = Xunit.Assert;
4+
5+
namespace EFCoreSecondLevelCacheInterceptor.UnitTests;
6+
7+
public class EFMessagePackSerializerTests
8+
{
9+
private static Mock<IOptions<EFCoreSecondLevelCacheSettings>> GetCacheSettingsMock(bool enableCompression)
10+
{
11+
var cacheSettingsMock = new Mock<IOptions<EFCoreSecondLevelCacheSettings>>();
12+
13+
cacheSettingsMock.Setup(c => c.Value)
14+
.Returns(new EFCoreSecondLevelCacheSettings
15+
{
16+
AdditionalData = new EFRedisCacheConfigurationOptions
17+
{
18+
EnableCompression = enableCompression
19+
}
20+
});
21+
22+
return cacheSettingsMock;
23+
}
24+
25+
[Fact]
26+
public void SerializeAndDeserializeWhenObjectIsComplexAndCompressionDisabledShouldReturnEqualObject()
27+
{
28+
// Arrange
29+
var serializer = new EFMessagePackSerializer(GetCacheSettingsMock(enableCompression: false).Object);
30+
31+
var originalObject = new Person
32+
{
33+
Id = 1,
34+
Name = "John Doe",
35+
BirthDate = new DateTimeOffset(year: 1990, month: 5, day: 20, hour: 10, minute: 30, second: 0,
36+
TimeSpan.FromHours(value: 3.5)),
37+
Salary = 60000.50m,
38+
UniqueId = Guid.NewGuid()
39+
};
40+
41+
// Act
42+
var serializedData = serializer.Serialize(originalObject);
43+
var deserializedObject = serializer.Deserialize<Person>(serializedData);
44+
45+
// Assert
46+
Assert.NotNull(serializedData);
47+
Assert.True(serializedData.Length > 0);
48+
Assert.NotNull(deserializedObject);
49+
Assert.Equal(originalObject, deserializedObject);
50+
}
51+
52+
[Fact]
53+
public void SerializeAndDeserializeWhenObjectIsComplexAndCompressionEnabledShouldReturnEqualObject()
54+
{
55+
// Arrange
56+
var serializer = new EFMessagePackSerializer(GetCacheSettingsMock(enableCompression: true).Object);
57+
58+
var originalObject = new Person
59+
{
60+
Id = 100,
61+
Name = "Jane Smith",
62+
BirthDate = new DateTimeOffset(year: 1990, month: 5, day: 20, hour: 10, minute: 30, second: 0,
63+
TimeSpan.FromHours(value: 3.5)),
64+
Salary = 120000.75m,
65+
UniqueId = Guid.NewGuid()
66+
};
67+
68+
// Act
69+
var serializedData = serializer.Serialize(originalObject);
70+
var deserializedObject = serializer.Deserialize<Person>(serializedData);
71+
72+
// Assert
73+
Assert.NotNull(serializedData);
74+
Assert.True(serializedData.Length > 0);
75+
Assert.NotNull(deserializedObject);
76+
Assert.Equal(originalObject, deserializedObject);
77+
}
78+
79+
[Fact]
80+
public void SerializeWhenObjectIsNullShouldReturnValidByteArrayForNull()
81+
{
82+
// Arrange
83+
var serializer = new EFMessagePackSerializer(GetCacheSettingsMock(enableCompression: false).Object);
84+
85+
// Act
86+
var serializedData = serializer.Serialize<object>(obj: null);
87+
var deserializedObject = serializer.Deserialize<object>(serializedData);
88+
89+
// Assert
90+
// MessagePack 'nil' is represented by a single byte 0xc0
91+
Assert.NotNull(serializedData);
92+
Assert.Single(serializedData);
93+
Assert.Equal(expected: 0xc0, serializedData[0]);
94+
Assert.Null(deserializedObject);
95+
}
96+
97+
[Fact]
98+
public void DeserializeWhenDataIsNullShouldReturnDefaultOfT()
99+
{
100+
// Arrange
101+
var serializer = new EFMessagePackSerializer(GetCacheSettingsMock(enableCompression: false).Object);
102+
103+
// Act
104+
var result = serializer.Deserialize<Person>(data: null);
105+
106+
// Assert
107+
Assert.Null(result); // default(Person) is null for reference types
108+
}
109+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace EFCoreSecondLevelCacheInterceptor.UnitTests;
2+
3+
public class Person
4+
{
5+
public int Id { get; set; }
6+
7+
public string Name { get; set; }
8+
9+
public DateTimeOffset BirthDate { get; set; }
10+
11+
public decimal Salary { get; set; }
12+
13+
public Guid UniqueId { get; set; }
14+
15+
public override bool Equals(object obj)
16+
=> obj is Person person && Id == person.Id && string.Equals(Name, person.Name, StringComparison.Ordinal) &&
17+
BirthDate == person.BirthDate && Salary == person.Salary && UniqueId == person.UniqueId;
18+
19+
public override int GetHashCode() => HashCode.Combine(Id, Name, BirthDate, Salary, UniqueId);
20+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using MessagePack;
2+
using MessagePack.Formatters;
3+
4+
namespace Issue123WithMessagePack;
5+
6+
public class DBNullFormatter : IMessagePackFormatter<DBNull?>
7+
{
8+
public static readonly DBNullFormatter Instance = new();
9+
10+
private DBNullFormatter()
11+
{
12+
}
13+
14+
public void Serialize(ref MessagePackWriter writer, DBNull? value, MessagePackSerializerOptions options)
15+
=> writer.WriteNil();
16+
17+
public DBNull Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) => DBNull.Value;
18+
}
Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
using Issue123WithMessagePack.Entities;
22
using Microsoft.EntityFrameworkCore;
33

4-
namespace Issue123WithMessagePack.DataLayer
4+
namespace Issue123WithMessagePack.DataLayer;
5+
6+
public class ApplicationDbContext : DbContext
57
{
6-
public class ApplicationDbContext : DbContext
8+
public ApplicationDbContext(DbContextOptions options) : base(options)
79
{
8-
public ApplicationDbContext(DbContextOptions options)
9-
: base(options)
10-
{
11-
}
12-
13-
public DbSet<Person> People { get; set; }
1410
}
11+
12+
public DbSet<Person> People { get; set; }
1513
}
Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,28 @@
1-
using Microsoft.EntityFrameworkCore.Design;
21
using Microsoft.EntityFrameworkCore;
2+
using Microsoft.EntityFrameworkCore.Design;
33
using Microsoft.Extensions.Configuration;
4-
using System;
5-
using System.IO;
64
using Microsoft.Extensions.DependencyInjection;
75

8-
namespace Issue123WithMessagePack.DataLayer
6+
namespace Issue123WithMessagePack.DataLayer;
7+
8+
public class MsSqlContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
99
{
10-
public class MsSqlContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
10+
public ApplicationDbContext CreateDbContext(string[] args)
1111
{
12-
public ApplicationDbContext CreateDbContext(string[] args)
13-
{
14-
var services = new ServiceCollection();
12+
var services = new ServiceCollection();
13+
14+
var basePath = Directory.GetCurrentDirectory();
15+
Console.WriteLine($"Using `{basePath}` as the ContentRootPath");
16+
17+
var configuration = new ConfigurationBuilder().SetBasePath(basePath)
18+
.AddJsonFile(path: "appsettings.json", optional: false, reloadOnChange: true)
19+
.Build();
20+
21+
services.AddSingleton(_ => configuration);
1522

16-
var basePath = Directory.GetCurrentDirectory();
17-
Console.WriteLine($"Using `{basePath}` as the ContentRootPath");
18-
var configuration = new ConfigurationBuilder()
19-
.SetBasePath(basePath)
20-
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
21-
.Build();
22-
services.AddSingleton(_ => configuration);
23+
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
24+
optionsBuilder.UseSqlServer(EFServiceProvider.GetConnectionString(basePath, configuration));
2325

24-
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
25-
optionsBuilder.UseSqlServer(EFServiceProvider.GetConnectionString(basePath, configuration));
26-
return new ApplicationDbContext(optionsBuilder.Options);
27-
}
26+
return new ApplicationDbContext(optionsBuilder.Options);
2827
}
2928
}

0 commit comments

Comments
 (0)