Skip to content

Commit db4fc93

Browse files
committed
[DEVEX-222] Simplified the message serializer implementation
Now serializer creates a derived serializer merging operation settings with default settings if needed. That allowed to hide internal details and there's no need to pass content type explicitly, as it's already in settings. Extended SchemaRegistry to provide wrapper methods to naming resolution context. Thanks to that MessageSerializer doesn't need to reference it internally. To make responsibilities clearer made also naming resolution context part of serializer context.
1 parent ee6d6e7 commit db4fc93

14 files changed

+272
-250
lines changed

Diff for: src/KurrentDB.Client/Core/KurrentDBClientSerializationSettings.cs

+12-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public class KurrentDBClientSerializationSettings {
7171
/// });
7272
/// </code>
7373
/// </example>
74-
public static KurrentDBClientSerializationSettings Default(
74+
public static KurrentDBClientSerializationSettings Get(
7575
Action<KurrentDBClientSerializationSettings>? configure = null
7676
) {
7777
var settings = new KurrentDBClientSerializationSettings();
@@ -280,6 +280,17 @@ public KurrentDBClientSerializationSettings UseMetadataType(Type type) {
280280

281281
return this;
282282
}
283+
/// <summary>
284+
/// Configures which serialization format (JSON or binary) is used by default when writing messages
285+
/// where the content type isn't explicitly specified. The default content type is "application/json"
286+
/// </summary>
287+
/// <param name="contentType">The serialization format content type</param>
288+
/// <returns>The current instance for method chaining.</returns>
289+
public KurrentDBClientSerializationSettings UseContentType(ContentType contentType) {
290+
DefaultContentType = contentType;
291+
292+
return this;
293+
}
283294

284295
/// <summary>
285296
/// Creates a deep copy of the current serialization settings.

Diff for: src/KurrentDB.Client/Core/KurrentDBClientSettings.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,6 @@ public partial class KurrentDBClientSettings {
6262
/// Provides configuration options for messages serialization and deserialization in the KurrentDB client.
6363
/// If null, default settings are used.
6464
/// </summary>
65-
public KurrentDBClientSerializationSettings Serialization { get; set; } = KurrentDBClientSerializationSettings.Default();
65+
public KurrentDBClientSerializationSettings Serialization { get; set; } = KurrentDBClientSerializationSettings.Get();
6666
}
6767
}

Diff for: src/KurrentDB.Client/Core/Serialization/MessageSerializer.cs

+38-52
Original file line numberDiff line numberDiff line change
@@ -11,78 +11,53 @@ interface IMessageSerializer {
1111
#else
1212
public bool TryDeserialize(EventRecord record, [NotNullWhen(true)] out Message? deserialized);
1313
#endif
14-
}
1514

16-
record MessageSerializationContext(
17-
string StreamName,
18-
ContentType ContentType
19-
) {
20-
public string CategoryName =>
21-
StreamName.Split('-').FirstOrDefault() ?? "no_stream_category";
15+
public IMessageSerializer With(OperationSerializationSettings? operationSettings);
2216
}
2317

18+
record MessageSerializationContext(MessageTypeNamingResolutionContext NamingResolution);
19+
2420
static class MessageSerializerExtensions {
2521
public static MessageData[] Serialize(
2622
this IMessageSerializer serializer,
2723
IEnumerable<Message> messages,
28-
MessageSerializationContext context
29-
) {
30-
return messages.Select(m => serializer.Serialize(m, context)).ToArray();
31-
}
32-
33-
public static IMessageSerializer With(
34-
this IMessageSerializer defaultMessageSerializer,
35-
KurrentDBClientSerializationSettings defaultSettings,
36-
OperationSerializationSettings? operationSettings
37-
) {
38-
if (operationSettings == null)
39-
return defaultMessageSerializer;
40-
41-
if (operationSettings.AutomaticDeserialization == AutomaticDeserialization.Disabled)
42-
return NullMessageSerializer.Instance;
43-
44-
if (operationSettings.ConfigureSettings == null)
45-
return defaultMessageSerializer;
46-
47-
var settings = defaultSettings.Clone();
48-
operationSettings.ConfigureSettings.Invoke(settings);
49-
50-
return new MessageSerializer(SchemaRegistry.From(settings));
51-
}
24+
MessageSerializationContext serializationContext
25+
) =>
26+
messages.Select(m => serializer.Serialize(m, serializationContext)).ToArray();
5227
}
5328

54-
class MessageSerializer(SchemaRegistry schemaRegistry) : IMessageSerializer {
29+
class MessageSerializer(SchemaRegistry schemaRegistry, KurrentDBClientSerializationSettings serializationSettings)
30+
: IMessageSerializer {
5531
readonly SystemTextJsonSerializer _metadataSerializer =
5632
new SystemTextJsonSerializer(
5733
new SystemTextJsonSerializationSettings { Options = KurrentDBClient.StreamMetadataJsonSerializerOptions }
5834
);
5935

60-
readonly IMessageTypeNamingStrategy _messageTypeNamingStrategy =
61-
schemaRegistry.MessageTypeNamingStrategy;
36+
readonly string _contentType = serializationSettings.DefaultContentType.ToMessageContentType();
6237

6338
public MessageData Serialize(Message message, MessageSerializationContext serializationContext) {
6439
var (data, metadata, messageId) = message;
6540

66-
var eventType = _messageTypeNamingStrategy
41+
var messageType = schemaRegistry
6742
.ResolveTypeName(
6843
message.Data.GetType(),
69-
new MessageTypeNamingResolutionContext(serializationContext.CategoryName)
44+
serializationContext.NamingResolution
7045
);
7146

7247
var serializedData = schemaRegistry
73-
.GetSerializer(serializationContext.ContentType)
48+
.GetSerializer(serializationSettings.DefaultContentType)
7449
.Serialize(data);
7550

7651
var serializedMetadata = metadata != null
7752
? _metadataSerializer.Serialize(metadata)
7853
: ReadOnlyMemory<byte>.Empty;
7954

8055
return new MessageData(
81-
eventType,
56+
messageType,
8257
serializedData,
8358
serializedMetadata,
8459
messageId,
85-
serializationContext.ContentType.ToMessageContentType()
60+
_contentType
8661
);
8762
}
8863

@@ -91,7 +66,7 @@ public bool TryDeserialize(EventRecord record, out Message? deserialized) {
9166
#else
9267
public bool TryDeserialize(EventRecord record, [NotNullWhen(true)] out Message? deserialized) {
9368
#endif
94-
if (!TryResolveClrType(record, out var clrType)) {
69+
if (!schemaRegistry.TryResolveClrType(record.EventType, out var clrType)) {
9570
deserialized = null;
9671
return false;
9772
}
@@ -105,29 +80,36 @@ public bool TryDeserialize(EventRecord record, [NotNullWhen(true)] out Message?
10580
return false;
10681
}
10782

108-
object? metadata = record.Metadata.Length > 0 && TryResolveClrMetadataType(record, out var clrMetadataType)
83+
object? metadata = record.Metadata.Length > 0
84+
&& schemaRegistry.TryResolveClrMetadataType(record.EventType, out var clrMetadataType)
10985
? _metadataSerializer.Deserialize(record.Metadata, clrMetadataType!)
11086
: null;
11187

11288
deserialized = Message.From(data, metadata, record.EventId);
11389
return true;
11490
}
11591

116-
public static MessageSerializer From(KurrentDBClientSerializationSettings? settings = null) {
117-
settings ??= KurrentDBClientSerializationSettings.Default();
92+
public IMessageSerializer With(OperationSerializationSettings? operationSettings) {
93+
if (operationSettings == null)
94+
return this;
95+
96+
if (operationSettings.AutomaticDeserialization == AutomaticDeserialization.Disabled)
97+
return NullMessageSerializer.Instance;
98+
99+
if (operationSettings.ConfigureSettings == null)
100+
return this;
118101

119-
return new MessageSerializer(SchemaRegistry.From(settings));
102+
var settings = serializationSettings.Clone();
103+
operationSettings.ConfigureSettings.Invoke(settings);
104+
105+
return new MessageSerializer(SchemaRegistry.From(settings), settings);
120106
}
121107

122-
bool TryResolveClrType(EventRecord record, out Type? clrType) =>
123-
schemaRegistry
124-
.MessageTypeNamingStrategy
125-
.TryResolveClrType(record.EventType, out clrType);
108+
public static MessageSerializer From(KurrentDBClientSerializationSettings? settings = null) {
109+
settings ??= KurrentDBClientSerializationSettings.Get();
126110

127-
bool TryResolveClrMetadataType(EventRecord record, out Type? clrMetadataType) =>
128-
schemaRegistry
129-
.MessageTypeNamingStrategy
130-
.TryResolveClrMetadataType(record.EventType, out clrMetadataType);
111+
return new MessageSerializer(SchemaRegistry.From(settings), settings);
112+
}
131113
}
132114

133115
class NullMessageSerializer : IMessageSerializer {
@@ -145,4 +127,8 @@ public bool TryDeserialize(EventRecord eventRecord, [NotNullWhen(true)] out Mess
145127
deserialized = null;
146128
return false;
147129
}
130+
131+
public IMessageSerializer With(OperationSerializationSettings? operationSettings) {
132+
return this;
133+
}
148134
}

Diff for: src/KurrentDB.Client/Core/Serialization/MessageTypeResolutionStrategy.cs

+8-6
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,18 @@ public interface IMessageTypeNamingStrategy {
1111
#else
1212
bool TryResolveClrType(string messageTypeName, [NotNullWhen(true)] out Type? type);
1313
#endif
14-
15-
14+
1615
#if NET48
1716
bool TryResolveClrMetadataType(string messageTypeName, out Type? type);
1817
#else
1918
bool TryResolveClrMetadataType(string messageTypeName, [NotNullWhen(true)] out Type? type);
2019
#endif
2120
}
2221

23-
public record MessageTypeNamingResolutionContext(string CategoryName);
22+
public record MessageTypeNamingResolutionContext(string CategoryName) {
23+
public static MessageTypeNamingResolutionContext FromStreamName(string streamName) =>
24+
new(streamName.Split('-').FirstOrDefault() ?? "no_stream_category");
25+
}
2426

2527
class MessageTypeNamingStrategyWrapper(
2628
IMessageTypeRegistry messageTypeRegistry,
@@ -66,9 +68,9 @@ public bool TryResolveClrMetadataType(string messageTypeName, [NotNullWhen(true)
6668

6769
public class DefaultMessageTypeNamingStrategy(Type? defaultMetadataType) : IMessageTypeNamingStrategy {
6870
readonly Type _defaultMetadataType = defaultMetadataType ?? typeof(TracingMetadata);
69-
71+
7072
public string ResolveTypeName(Type messageType, MessageTypeNamingResolutionContext resolutionContext) =>
71-
$"{resolutionContext.CategoryName}-{messageType.FullName}";
73+
$"{resolutionContext.CategoryName}-{messageType.FullName}";
7274

7375
#if NET48
7476
public bool TryResolveClrType(string messageTypeName, out Type? type) {
@@ -83,7 +85,7 @@ public bool TryResolveClrType(string messageTypeName, [NotNullWhen(true)] out Ty
8385
}
8486

8587
var clrTypeName = messageTypeName[(categorySeparatorIndex + 1)..];
86-
88+
8789
type = TypeProvider.GetTypeByFullName(clrTypeName);
8890

8991
return type != null;

Diff for: src/KurrentDB.Client/Core/Serialization/SchemaRegistry.cs

+18-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using KurrentDB.Client;
1+
using System.Diagnostics.CodeAnalysis;
22

33
namespace KurrentDB.Client.Core.Serialization;
44

@@ -30,11 +30,26 @@ class SchemaRegistry(
3030
IDictionary<ContentType, ISerializer> serializers,
3131
IMessageTypeNamingStrategy messageTypeNamingStrategy
3232
) {
33-
public IMessageTypeNamingStrategy MessageTypeNamingStrategy { get; } = messageTypeNamingStrategy;
34-
3533
public ISerializer GetSerializer(ContentType schemaType) =>
3634
serializers[schemaType];
3735

36+
public string ResolveTypeName(Type messageType, MessageTypeNamingResolutionContext resolutionContext) =>
37+
messageTypeNamingStrategy.ResolveTypeName(messageType, resolutionContext);
38+
39+
#if NET48
40+
public bool TryResolveClrType(string messageTypeName, out Type? type) =>
41+
#else
42+
public bool TryResolveClrType(string messageTypeName, [NotNullWhen(true)] out Type? type) =>
43+
#endif
44+
messageTypeNamingStrategy.TryResolveClrType(messageTypeName, out type);
45+
46+
#if NET48
47+
public bool TryResolveClrMetadataType(string messageTypeName, out Type? type) =>
48+
#else
49+
public bool TryResolveClrMetadataType(string messageTypeName, [NotNullWhen(true)] out Type? type) =>
50+
#endif
51+
messageTypeNamingStrategy.TryResolveClrMetadataType(messageTypeName, out type);
52+
3853
public static SchemaRegistry From(KurrentDBClientSerializationSettings settings) {
3954
var messageTypeNamingStrategy =
4055
settings.MessageTypeNamingStrategy ?? new DefaultMessageTypeNamingStrategy(settings.DefaultMetadataType);

Diff for: src/KurrentDB.Client/PersistentSubscriptions/KurrentDBPersistentSubscriptionsClient.Read.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ public PersistentSubscriptionResult SubscribeToStream(
269269
new() { Options = readOptions },
270270
Settings,
271271
options.UserCredentials,
272-
_messageSerializer.With(Settings.Serialization, options.SerializationSettings),
272+
_messageSerializer.With(options.SerializationSettings),
273273
cancellationToken
274274
);
275275
}

Diff for: src/KurrentDB.Client/Streams/KurrentDBClient.Append.cs

+6-7
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Kurrent.Diagnostics.Tracing;
1313
using static EventStore.Client.Streams.AppendResp.Types.WrongExpectedVersion;
1414
using static EventStore.Client.Streams.Streams;
15+
using static KurrentDB.Client.Core.Serialization.MessageTypeNamingResolutionContext;
1516

1617
namespace KurrentDB.Client {
1718
public partial class KurrentDBClient {
@@ -31,19 +32,17 @@ public Task<IWriteResult> AppendToStreamAsync(
3132
AppendToStreamOptions? options = null,
3233
CancellationToken cancellationToken = default
3334
) {
34-
var serializationContext
35-
= new MessageSerializationContext(streamName, Settings.Serialization.DefaultContentType);
36-
37-
var messageSerializer = _messageSerializer.With(Settings.Serialization, options?.SerializationSettings);
38-
39-
var messageData = messageSerializer.Serialize(messages, serializationContext);
35+
var messageSerializationContext = new MessageSerializationContext(FromStreamName(streamName));
36+
37+
var messageData = _messageSerializer.With(options?.SerializationSettings)
38+
.Serialize(messages, messageSerializationContext);
4039

4140
return AppendToStreamAsync(streamName, expectedState, messageData, options, cancellationToken);
4241
}
4342

4443
/// <summary>
4544
/// Appends events asynchronously to a stream using raw message data.
46-
/// If you want to use auto-serialization, use overload with <see cref="KurrentDB.Client.Core.Serialization.Message"/>.</param>.
45+
/// If you want to use auto-serialization, use overload with <see cref="Message"/>.</param>.
4746
/// This method intends to cover low-level scenarios in which you want to have full control of the serialization mechanism.
4847
/// </summary>
4948
/// <param name="streamName">The name of the stream to append events to.</param>

Diff for: src/KurrentDB.Client/Streams/KurrentDBClient.Read.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public ReadAllStreamResult ReadAllAsync(
5252
readReq,
5353
Settings,
5454
options,
55-
_messageSerializer.With(Settings.Serialization, options.SerializationSettings),
55+
_messageSerializer.With(options.SerializationSettings),
5656
cancellationToken
5757
);
5858
}
@@ -229,7 +229,7 @@ public ReadStreamResult ReadStreamAsync(
229229
Settings,
230230
options.Deadline,
231231
options.UserCredentials,
232-
_messageSerializer.With(Settings.Serialization, options.SerializationSettings),
232+
_messageSerializer.With(options.SerializationSettings),
233233
cancellationToken
234234
);
235235
}

Diff for: src/KurrentDB.Client/Streams/KurrentDBClient.Subscriptions.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ public StreamSubscriptionResult SubscribeToAll(
190190
},
191191
Settings,
192192
options.UserCredentials,
193-
_messageSerializer.With(Settings.Serialization, options.SerializationSettings),
193+
_messageSerializer.With(options.SerializationSettings),
194194
cancellationToken
195195
);
196196

@@ -329,7 +329,7 @@ public StreamSubscriptionResult SubscribeToStream(
329329
},
330330
Settings,
331331
options.UserCredentials,
332-
_messageSerializer.With(Settings.Serialization, options.SerializationSettings),
332+
_messageSerializer.With(options.SerializationSettings),
333333
cancellationToken
334334
);
335335

0 commit comments

Comments
 (0)