3
3
4
4
namespace EventStore . Client ;
5
5
6
- public enum AutomaticDeserialization {
7
- Disabled = 0 ,
8
- Enabled = 1
9
- }
10
-
6
+ /// <summary>
7
+ /// Provides configuration options for messages serialization and deserialization in the KurrentDB client.
8
+ /// </summary>
11
9
public class KurrentClientSerializationSettings {
12
- public ISerializer ? JsonSerializer { get ; set ; }
13
- public ISerializer ? BytesSerializer { get ; set ; }
14
- public ContentType DefaultContentType { get ; set ; } = ContentType . Json ;
10
+ /// <summary>
11
+ /// The serializer responsible for handling JSON-formatted data. This serializer is used both for
12
+ /// serializing outgoing JSON messages and deserializing incoming JSON messages. If not specified,
13
+ /// a default System.Text.Json serializer will be used with standard settings.
14
+ /// <br/>
15
+ /// That also allows you to bring your custom JSON serializer implementation (e.g. JSON.NET)
16
+ /// </summary>
17
+ public ISerializer ? JsonSerializer { get ; set ; }
18
+
19
+ /// <summary>
20
+ /// The serializer responsible for handling binary data formats. This is used when working with
21
+ /// binary-encoded messages rather than text-based formats (e.g. Protobuf or Avro). Required when storing
22
+ /// or retrieving content with "application/octet-stream" content type
23
+ /// </summary>
24
+ public ISerializer ? BytesSerializer { get ; set ; }
25
+
26
+ /// <summary>
27
+ /// Determines which serialization format (JSON or binary) is used by default when writing messages
28
+ /// where the content type isn't explicitly specified. The default content type is "application/json"
29
+ /// </summary>
30
+ public ContentType DefaultContentType { get ; set ; } = ContentType . Json ;
31
+
32
+ /// <summary>
33
+ /// Defines the custom strategy used to map between the type name stored in messages and .NET type names.
34
+ /// If not provided the default <see cref="Kurrent.Client.Core.Serialization.DefaultMessageTypeNamingStrategy"/> will be used.
35
+ /// It resolves the CLR type name to the format: "{stream category name}-{CLR Message Type}".
36
+ /// You can provide your own implementation of <see cref="Kurrent.Client.Core.Serialization.IMessageTypeNamingStrategy"/>
37
+ /// and register it here to override the default behaviour
38
+ /// </summary>
15
39
public IMessageTypeNamingStrategy ? MessageTypeNamingStrategy { get ; set ; }
16
- public IDictionary < Type , string > MessageTypeMap { get ; set ; } = new Dictionary < Type , string > ( ) ;
17
- public IDictionary < string , Type [ ] > CategoryMessageTypesMap { get ; set ; } = new Dictionary < string , Type [ ] > ( ) ;
18
- public Type ? DefaultMetadataType { get ; set ; }
19
40
41
+ /// <summary>
42
+ /// Allows to register mapping of CLR message types to their corresponding message type names used in serialized messages.
43
+ /// </summary>
44
+ public IDictionary < Type , string > MessageTypeMap { get ; set ; } = new Dictionary < Type , string > ( ) ;
45
+
46
+ /// <summary>
47
+ /// Registers CLR message types that can be appended to the specific stream category.
48
+ /// Types will have message type names resolved based on the used <see cref="Kurrent.Client.Core.Serialization.IMessageTypeNamingStrategy"/>
49
+ /// </summary>
50
+ public IDictionary < string , Type [ ] > CategoryMessageTypesMap { get ; set ; } = new Dictionary < string , Type [ ] > ( ) ;
51
+
52
+ /// <summary>
53
+ /// Specifies the CLR type that should be used when deserializing metadata for all events.
54
+ /// When set, the client will attempt to deserialize event metadata into this type.
55
+ /// If not provided, <see cref="Kurrent.Diagnostics.Tracing.TracingMetadata"/> will be used.
56
+ /// </summary>
57
+ public Type ? DefaultMetadataType { get ; set ; }
58
+
59
+ /// <summary>
60
+ /// Creates a new instance of serialization settings with either default values or custom configuration.
61
+ /// This factory method is the recommended way to create serialization settings for the KurrentDB client.
62
+ /// </summary>
63
+ /// <param name="configure">Optional callback to customize the settings. If null, default settings are used.</param>
64
+ /// <returns>A fully configured instance ready to be used with the KurrentDB client.</returns>
65
+ /// <example>
66
+ /// <code>
67
+ /// var settings = KurrentClientSerializationSettings.Default(options => {
68
+ /// options.RegisterMessageType<UserCreated>("user-created");
69
+ /// options.RegisterMessageType<UserUpdated>("user-updated");
70
+ /// options.RegisterMessageTypeForCategory<UserCreated>("user");
71
+ /// });
72
+ /// </code>
73
+ /// </example>
20
74
public static KurrentClientSerializationSettings Default (
21
75
Action < KurrentClientSerializationSettings > ? configure = null
22
76
) {
@@ -27,6 +81,22 @@ public static KurrentClientSerializationSettings Default(
27
81
return settings ;
28
82
}
29
83
84
+ /// <summary>
85
+ /// Configures the JSON serializer using custom options while inheriting from the default System.Text.Json settings.
86
+ /// This allows fine-tuning serialization behavior such as case sensitivity, property naming, etc.
87
+ /// </summary>
88
+ /// <param name="configure">A function that receives the default options and returns modified options.</param>
89
+ /// <returns>The current instance for method chaining.</returns>
90
+ /// <example>
91
+ /// <code>
92
+ /// settings.UseJsonSettings(options => {
93
+ /// options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
94
+ /// options.WriteIndented = true;
95
+ /// options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
96
+ /// return options;
97
+ /// });
98
+ /// </code>
99
+ /// </example>
30
100
public KurrentClientSerializationSettings UseJsonSettings (
31
101
Func < JsonSerializerOptions , JsonSerializerOptions > configure
32
102
) {
@@ -38,6 +108,11 @@ Func<JsonSerializerOptions, JsonSerializerOptions> configure
38
108
return this ;
39
109
}
40
110
111
+ /// <summary>
112
+ /// Configures JSON serialization using provided System.Text.Json serializer options.
113
+ /// </summary>
114
+ /// <param name="systemTextJsonSerializerOptions">The JSON serializer options to use.</param>
115
+ /// <returns>The current instance for method chaining.</returns>
41
116
public KurrentClientSerializationSettings UseJsonSettings ( JsonSerializerOptions systemTextJsonSerializerOptions ) {
42
117
JsonSerializer = new SystemTextJsonSerializer (
43
118
new SystemTextJsonSerializationSettings { Options = systemTextJsonSerializerOptions }
@@ -46,6 +121,11 @@ public KurrentClientSerializationSettings UseJsonSettings(JsonSerializerOptions
46
121
return this ;
47
122
}
48
123
124
+ /// <summary>
125
+ /// Configures JSON serialization using provided <see cref="Kurrent.Client.Core.Serialization.SystemTextJsonSerializationSettings"/>
126
+ /// </summary>
127
+ /// <param name="jsonSerializationSettings">The SystemTextJson serialization settings to use.</param>
128
+ /// <returns>The current instance for method chaining.</returns>
49
129
public KurrentClientSerializationSettings UseJsonSettings (
50
130
SystemTextJsonSerializationSettings jsonSerializationSettings
51
131
) {
@@ -54,22 +134,44 @@ SystemTextJsonSerializationSettings jsonSerializationSettings
54
134
return this ;
55
135
}
56
136
137
+ /// <summary>
138
+ /// Sets a custom JSON serializer implementation.
139
+ /// That also allows you to bring your custom JSON serializer implementation (e.g. JSON.NET)
140
+ /// </summary>
141
+ /// <param name="serializer">The serializer to use for JSON content.</param>
142
+ /// <returns>The current instance for method chaining.</returns>
57
143
public KurrentClientSerializationSettings UseJsonSerializer ( ISerializer serializer ) {
58
144
JsonSerializer = serializer ;
59
145
60
146
return this ;
61
147
}
62
148
149
+ /// <summary>
150
+ /// Sets a custom binary serializer implementation.
151
+ /// That also allows you to bring your custom binary serializer implementation (e.g. Protobuf or Avro)
152
+ /// </summary>
153
+ /// <param name="serializer">The serializer to use for binary content.</param>
154
+ /// <returns>The current instance for method chaining.</returns>
63
155
public KurrentClientSerializationSettings UseBytesSerializer ( ISerializer serializer ) {
64
156
BytesSerializer = serializer ;
65
157
66
158
return this ;
67
159
}
68
160
161
+ /// <summary>
162
+ /// Configures a custom message type naming strategy.
163
+ /// </summary>
164
+ /// <typeparam name="TCustomMessageTypeResolutionStrategy">The type of naming strategy to use.</typeparam>
165
+ /// <returns>The current instance for method chaining.</returns>
69
166
public KurrentClientSerializationSettings UseMessageTypeNamingStrategy < TCustomMessageTypeResolutionStrategy > ( )
70
167
where TCustomMessageTypeResolutionStrategy : IMessageTypeNamingStrategy , new ( ) =>
71
168
UseMessageTypeNamingStrategy ( new TCustomMessageTypeResolutionStrategy ( ) ) ;
72
169
170
+ /// <summary>
171
+ /// Configures a custom message type naming strategy.
172
+ /// </summary>
173
+ /// <param name="messageTypeNamingStrategy">The naming strategy instance to use.</param>
174
+ /// <returns>The current instance for method chaining.</returns>
73
175
public KurrentClientSerializationSettings UseMessageTypeNamingStrategy (
74
176
IMessageTypeNamingStrategy messageTypeNamingStrategy
75
177
) {
@@ -78,9 +180,31 @@ IMessageTypeNamingStrategy messageTypeNamingStrategy
78
180
return this ;
79
181
}
80
182
183
+ /// <summary>
184
+ /// Associates a message type with a specific stream category to enable automatic deserialization.
185
+ /// In event sourcing, streams are often prefixed with a category (e.g., "user-123", "order-456").
186
+ /// This method tells the client which message types can appear in streams of a given category.
187
+ /// </summary>
188
+ /// <typeparam name="T">The event or message type that can appear in the category's streams.</typeparam>
189
+ /// <param name="categoryName">The category prefix (e.g., "user", "order", "account").</param>
190
+ /// <returns>The current instance for method chaining.</returns>
191
+ /// <example>
192
+ /// <code>
193
+ /// // Register event types that can appear in user streams
194
+ /// settings.RegisterMessageTypeForCategory<UserCreated>("user")
195
+ /// .RegisterMessageTypeForCategory<UserUpdated>("user")
196
+ /// .RegisterMessageTypeForCategory<UserDeleted>("user");
197
+ /// </code>
198
+ /// </example>
81
199
public KurrentClientSerializationSettings RegisterMessageTypeForCategory < T > ( string categoryName ) =>
82
200
RegisterMessageTypeForCategory ( categoryName , typeof ( T ) ) ;
83
201
202
+ /// <summary>
203
+ /// Registers multiple message types for a specific stream category.
204
+ /// </summary>
205
+ /// <param name="categoryName">The category name to register the types with.</param>
206
+ /// <param name="types">The message types to register.</param>
207
+ /// <returns>The current instance for method chaining.</returns>
84
208
public KurrentClientSerializationSettings RegisterMessageTypeForCategory ( string categoryName , params Type [ ] types ) {
85
209
CategoryMessageTypesMap [ categoryName ] = CategoryMessageTypesMap . TryGetValue ( categoryName , out var current )
86
210
? [ ..current , ..types ]
@@ -89,15 +213,45 @@ public KurrentClientSerializationSettings RegisterMessageTypeForCategory(string
89
213
return this ;
90
214
}
91
215
216
+ /// <summary>
217
+ /// Maps a .NET type to a specific message type name that will be stored in the message metadata.
218
+ /// This mapping is used during automatic deserialization, as it tells the client which CLR type
219
+ /// to instantiate when encountering a message with a particular type name in the database.
220
+ /// </summary>
221
+ /// <typeparam name="T">The .NET type to register (typically a message class).</typeparam>
222
+ /// <param name="typeName">The string identifier to use for this type in the database.</param>
223
+ /// <returns>The current instance for method chaining.</returns>
224
+ /// <remarks>
225
+ /// The type name is often different from the .NET type name to support versioning and evolution
226
+ /// of your domain model without breaking existing stored messages.
227
+ /// </remarks>
228
+ /// <example>
229
+ /// <code>
230
+ /// // Register me types with their corresponding type identifiers
231
+ /// settings.RegisterMessageType<UserCreated>("user-created-v1")
232
+ /// .RegisterMessageType<OrderPlaced>("order-placed-v2");
233
+ /// </code>
234
+ /// </example>
92
235
public KurrentClientSerializationSettings RegisterMessageType < T > ( string typeName ) =>
93
236
RegisterMessageType ( typeof ( T ) , typeName ) ;
94
237
238
+ /// <summary>
239
+ /// Registers a message type with a specific type name.
240
+ /// </summary>
241
+ /// <param name="type">The message type to register.</param>
242
+ /// <param name="typeName">The type name to register for the message type.</param>
243
+ /// <returns>The current instance for method chaining.</returns>
95
244
public KurrentClientSerializationSettings RegisterMessageType ( Type type , string typeName ) {
96
245
MessageTypeMap [ type ] = typeName ;
97
246
98
247
return this ;
99
248
}
100
249
250
+ /// <summary>
251
+ /// Registers multiple message types with their corresponding type names.
252
+ /// </summary>
253
+ /// <param name="typeMap">Dictionary mapping types to their type names.</param>
254
+ /// <returns>The current instance for method chaining.</returns>
101
255
public KurrentClientSerializationSettings RegisterMessageTypes ( IDictionary < Type , string > typeMap ) {
102
256
foreach ( var map in typeMap ) {
103
257
MessageTypeMap [ map . Key ] = map . Value ;
@@ -106,15 +260,31 @@ public KurrentClientSerializationSettings RegisterMessageTypes(IDictionary<Type,
106
260
return this ;
107
261
}
108
262
263
+ /// <summary>
264
+ /// Configures a strongly-typed metadata class for all mes in the system.
265
+ /// This enables accessing metadata properties in a type-safe manner rather than using dynamic objects.
266
+ /// </summary>
267
+ /// <typeparam name="T">The metadata class type containing properties matching the expected metadata fields.</typeparam>
268
+ /// <returns>The current instance for method chaining.</returns>
109
269
public KurrentClientSerializationSettings UseMetadataType < T > ( ) =>
110
270
UseMetadataType ( typeof ( T ) ) ;
111
271
272
+ /// <summary>
273
+ /// Configures a strongly-typed metadata class for all mes in the system.
274
+ /// This enables accessing metadata properties in a type-safe manner rather than using dynamic objects.
275
+ /// </summary>
276
+ /// <param name="type">The metadata class type containing properties matching the expected metadata fields.</param>
277
+ /// <returns>The current instance for method chaining.</returns>
112
278
public KurrentClientSerializationSettings UseMetadataType ( Type type ) {
113
279
DefaultMetadataType = type ;
114
280
115
281
return this ;
116
282
}
117
283
284
+ /// <summary>
285
+ /// Creates a deep copy of the current serialization settings.
286
+ /// </summary>
287
+ /// <returns>A new instance with copied settings.</returns>
118
288
internal KurrentClientSerializationSettings Clone ( ) {
119
289
return new KurrentClientSerializationSettings {
120
290
BytesSerializer = BytesSerializer ,
@@ -127,17 +297,64 @@ internal KurrentClientSerializationSettings Clone() {
127
297
}
128
298
}
129
299
300
+ /// <summary>
301
+ /// Provides operation-specific serialization settings that override the global client configuration
302
+ /// for individual operations like reading from or appending to streams. This allows fine-tuning
303
+ /// serialization behavior on a per-operation basis without changing the client-wide settings.
304
+ /// </summary>
130
305
public class OperationSerializationSettings {
306
+ /// <summary>
307
+ /// Controls whether mes should be automatically deserialized for this specific operation.
308
+ /// When enabled (the default), messages will be converted to their appropriate CLR types.
309
+ /// When disabled, messages will be returned in their raw serialized form.
310
+ /// </summary>
131
311
public AutomaticDeserialization AutomaticDeserialization { get ; private set ; } = AutomaticDeserialization . Enabled ;
312
+
313
+ /// <summary>
314
+ /// A callback that allows customizing serialization settings for this specific operation.
315
+ /// This can be used to override type mappings, serializers, or other settings just for
316
+ /// the scope of a single operation without affecting other operations.
317
+ /// </summary>
132
318
public Action < KurrentClientSerializationSettings > ? ConfigureSettings { get ; private set ; }
133
319
320
+ /// <summary>
321
+ /// A pre-configured settings instance that disables automatic deserialization.
322
+ /// Use this when you need to access raw message data in its serialized form.
323
+ /// </summary>
134
324
public static readonly OperationSerializationSettings Disabled = new OperationSerializationSettings {
135
325
AutomaticDeserialization = AutomaticDeserialization . Disabled
136
326
} ;
137
327
328
+ /// <summary>
329
+ /// Creates operation-specific serialization settings with custom configuration while keeping
330
+ /// automatic deserialization enabled. This allows operation-specific type mappings or
331
+ /// serializer settings without changing the global client configuration.
332
+ /// </summary>
333
+ /// <param name="configure">A callback to customize serialization settings for this operation.</param>
334
+ /// <returns>A configured instance of <see cref="OperationSerializationSettings"/> with enabled deserialization.</returns>
138
335
public static OperationSerializationSettings Configure ( Action < KurrentClientSerializationSettings > configure ) =>
139
336
new OperationSerializationSettings {
140
337
AutomaticDeserialization = AutomaticDeserialization . Enabled ,
141
338
ConfigureSettings = configure
142
339
} ;
143
340
}
341
+
342
+ /// <summary>
343
+ /// Controls whether the KurrentDB client should automatically deserialize message payloads
344
+ /// into their corresponding CLR types based on the configured type mappings.
345
+ /// </summary>
346
+ public enum AutomaticDeserialization {
347
+ /// <summary>
348
+ /// Disables automatic deserialization. Messages will be returned in their raw serialized form,
349
+ /// requiring manual deserialization by the application. Use this when you need direct access to the raw data
350
+ /// or when working with messages that don't have registered type mappings.
351
+ /// </summary>
352
+ Disabled = 0 ,
353
+
354
+ /// <summary>
355
+ /// Enables automatic deserialization. The client will attempt to convert messages into their appropriate
356
+ /// CLR types using the configured serializers and type mappings. This simplifies working with strongly-typed
357
+ /// domain messages but requires proper type registration.
358
+ /// </summary>
359
+ Enabled = 1
360
+ }
0 commit comments