Skip to content

Commit 551b974

Browse files
committed
Adding ITypeNameSerializer to control how message types are converted to / from strings
1 parent 9699c39 commit 551b974

File tree

4 files changed

+61
-39
lines changed

4 files changed

+61
-39
lines changed

src/Foundatio.TestHarness/Messaging/MessageBusTestBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ await messageBus.PublishAsync(new DerivedSimpleMessageA {
108108

109109
public virtual async Task CanSendMappedMessageAsync() {
110110
var messageBus = GetMessageBus(b => {
111-
b.MessageTypeMappings.Add(nameof(SimpleMessageA), typeof(SimpleMessageA));
111+
b.TypeNameSerializer = new DefaultTypeNameSerializer(_logger, new Dictionary<string, Type> {{ nameof(SimpleMessageA), typeof(SimpleMessageA) }});
112112
return b;
113113
});
114114
if (messageBus == null)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System;
2+
using System.Collections.Concurrent;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using Microsoft.Extensions.Logging;
6+
using Microsoft.Extensions.Logging.Abstractions;
7+
8+
namespace Foundatio.Messaging {
9+
public interface ITypeNameSerializer {
10+
string Serialize(Type type);
11+
Type Deserialize(string typeName);
12+
}
13+
14+
public class DefaultTypeNameSerializer : ITypeNameSerializer {
15+
private readonly Dictionary<string, Type> _typeNameOverrides;
16+
private readonly ILogger _logger;
17+
18+
public DefaultTypeNameSerializer(ILogger logger = null, IDictionary<string, Type> typeNameOverrides = null) {
19+
_logger = logger ?? NullLogger.Instance;
20+
_typeNameOverrides = typeNameOverrides != null ? new Dictionary<string, Type>(typeNameOverrides) : new Dictionary<string, Type>();
21+
}
22+
23+
private readonly ConcurrentDictionary<string, Type> _knownMessageTypesCache = new ConcurrentDictionary<string, Type>();
24+
public Type Deserialize(string typeName) {
25+
return _knownMessageTypesCache.GetOrAdd(typeName, newTypeName => {
26+
if (_typeNameOverrides != null && _typeNameOverrides.ContainsKey(newTypeName))
27+
return _typeNameOverrides[newTypeName];
28+
29+
try {
30+
return Type.GetType(newTypeName);
31+
} catch (Exception ex) {
32+
if (_logger.IsEnabled(LogLevel.Warning))
33+
_logger.LogWarning(ex, "Error getting message type: {MessageType}", newTypeName);
34+
35+
return null;
36+
}
37+
});
38+
}
39+
40+
private readonly ConcurrentDictionary<Type, string> _mappedMessageTypesCache = new ConcurrentDictionary<Type, string>();
41+
public string Serialize(Type type) {
42+
return _mappedMessageTypesCache.GetOrAdd(type, newType => {
43+
var reversedMap = _typeNameOverrides.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
44+
if (reversedMap.ContainsKey(newType))
45+
return reversedMap[newType];
46+
47+
return String.Concat(type.FullName, ", ", type.Assembly.GetName().Name);
48+
});
49+
}
50+
}
51+
}

src/Foundatio/Messaging/MessageBusBase.cs

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ public abstract class MessageBusBase<TOptions> : IMessageBus, IDisposable where
1818
protected readonly TOptions _options;
1919
protected readonly ILogger _logger;
2020
protected readonly ISerializer _serializer;
21+
protected readonly ITypeNameSerializer _typeNameSerializer;
2122
private bool _isDisposed;
2223

2324
public MessageBusBase(TOptions options) {
2425
_options = options ?? throw new ArgumentNullException(nameof(options));
2526
var loggerFactory = options?.LoggerFactory ?? NullLoggerFactory.Instance;
2627
_logger = loggerFactory.CreateLogger(GetType());
2728
_serializer = options.Serializer ?? DefaultSerializer.Instance;
29+
_typeNameSerializer = options.TypeNameSerializer ?? new DefaultTypeNameSerializer(_logger);
2830
MessageBusId = _options.Topic + Guid.NewGuid().ToString("N").Substring(10);
2931
_messageBusDisposedCancellationTokenSource = new CancellationTokenSource();
3032
}
@@ -39,32 +41,12 @@ public async Task PublishAsync(Type messageType, object message, TimeSpan? delay
3941
await PublishImplAsync(messageType, message, delay, cancellationToken).AnyContext();
4042
}
4143

42-
private readonly ConcurrentDictionary<Type, string> _mappedMessageTypesCache = new ConcurrentDictionary<Type, string>();
4344
protected string GetMappedMessageType(Type messageType) {
44-
return _mappedMessageTypesCache.GetOrAdd(messageType, type => {
45-
var reversedMap = _options.MessageTypeMappings.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
46-
if (reversedMap.ContainsKey(type))
47-
return reversedMap[type];
48-
49-
return String.Concat(messageType.FullName, ", ", messageType.Assembly.GetName().Name);
50-
});
45+
return _typeNameSerializer.Serialize(messageType);
5146
}
5247

53-
private readonly ConcurrentDictionary<string, Type> _knownMessageTypesCache = new ConcurrentDictionary<string, Type>();
5448
protected Type GetMappedMessageType(string messageType) {
55-
return _knownMessageTypesCache.GetOrAdd(messageType, type => {
56-
if (_options.MessageTypeMappings != null && _options.MessageTypeMappings.ContainsKey(type))
57-
return _options.MessageTypeMappings[type];
58-
59-
try {
60-
return Type.GetType(type);
61-
} catch (Exception ex) {
62-
if (_logger.IsEnabled(LogLevel.Warning))
63-
_logger.LogWarning(ex, "Error getting message body type: {MessageType}", type);
64-
65-
return null;
66-
}
67-
});
49+
return _typeNameSerializer.Deserialize(messageType);
6850
}
6951

7052
protected virtual Task EnsureTopicSubscriptionAsync<T>(CancellationToken cancellationToken) where T : class => Task.CompletedTask;

src/Foundatio/Messaging/SharedMessageBusOptions.cs

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ public class SharedMessageBusOptions : SharedOptions {
99
public string Topic { get; set; } = "messages";
1010

1111
/// <summary>
12-
/// Controls which types messages are mapped to.
12+
/// Controls how message types are serialized to/from strings.
1313
/// </summary>
14-
public Dictionary<string, Type> MessageTypeMappings { get; set; } = new Dictionary<string, Type>();
14+
public ITypeNameSerializer TypeNameSerializer { get; set; }
1515
}
1616

1717
public class SharedMessageBusOptionsBuilder<TOptions, TBuilder> : SharedOptionsBuilder<TOptions, TBuilder>
@@ -23,20 +23,9 @@ public TBuilder Topic(string topic) {
2323
Target.Topic = topic;
2424
return (TBuilder)this;
2525
}
26-
27-
public TBuilder MapMessageType<T>(string name) {
28-
if (Target.MessageTypeMappings == null)
29-
Target.MessageTypeMappings = new Dictionary<string, Type>();
30-
31-
Target.MessageTypeMappings[name] = typeof(T);
32-
return (TBuilder)this;
33-
}
34-
35-
public TBuilder MapMessageTypeToClassName<T>() {
36-
if (Target.MessageTypeMappings == null)
37-
Target.MessageTypeMappings = new Dictionary<string, Type>();
38-
39-
Target.MessageTypeMappings[typeof(T).Name] = typeof(T);
26+
27+
public TBuilder TypeNameSerializer(ITypeNameSerializer typeNameSerializer) {
28+
Target.TypeNameSerializer = typeNameSerializer;
4029
return (TBuilder)this;
4130
}
4231
}

0 commit comments

Comments
 (0)