diff --git a/CryptoExchange.Net/Sockets/Default/SocketConnection.cs b/CryptoExchange.Net/Sockets/Default/SocketConnection.cs index a82dc44..b1fa7db 100644 --- a/CryptoExchange.Net/Sockets/Default/SocketConnection.cs +++ b/CryptoExchange.Net/Sockets/Default/SocketConnection.cs @@ -9,6 +9,9 @@ using CryptoExchange.Net.Sockets.Interfaces; using Microsoft.Extensions.Logging; using System; using System.Collections; +#if NET8_0_OR_GREATER +using System.Collections.Frozen; +#endif using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data; @@ -263,6 +266,10 @@ namespace CryptoExchange.Net.Sockets.Default #else private readonly object _listenersLock = new object(); #endif + + + private RouteTable _routeTable = new RouteTable([]); + private ReadOnlyCollection _listeners; private readonly ILogger _logger; private SocketStatus _status; @@ -530,31 +537,22 @@ namespace CryptoExchange.Net.Sockets.Default return; } - Type? deserializationType = null; - foreach (var listener in _listeners) - { - var routes = listener.MessageRouter[typeIdentifier]; - deserializationType = routes?.RouteType; - if (deserializationType != null) - break; - } - - if (deserializationType == null) + var routingEntry = _routeTable.GetRouteTableEntry(typeIdentifier); + if (routingEntry == null) { if (!ApiClient.HandleUnhandledMessage(this, typeIdentifier, data)) { // No handler found for identifier either, can't process _logger.LogWarning("Failed to determine message type for identifier {Identifier}. Data: {Message}", typeIdentifier, Encoding.UTF8.GetString(data.ToArray())); } - + return; } - object result; try { - if (deserializationType == typeof(string)) + if (routingEntry.RouteType == typeof(string)) { #if NETSTANDARD2_0 result = Encoding.UTF8.GetString(data.ToArray()); @@ -564,7 +562,7 @@ namespace CryptoExchange.Net.Sockets.Default } else { - result = messageConverter.Deserialize(data, deserializationType); + result = messageConverter.Deserialize(data, routingEntry.RouteType); } } catch(Exception ex) @@ -581,22 +579,12 @@ namespace CryptoExchange.Net.Sockets.Default } var topicFilter = messageConverter.GetTopicFilter(result); - - bool processed = false; - foreach (var listener in _listeners) + var processed = false; + foreach (var handler in routingEntry.Handlers) { - var routeMap = listener.MessageRouter[typeIdentifier]; - if (routeMap != null) - { - // Could be null if listeners was updated while handling message - var handled = listener.Handle(topicFilter, this, receiveTime, originalData, result, routeMap); - if (!processed) - processed = handled; - - if (!routeMap.MultipleReaders) - // If this was a response to a query and there are no other routes expecting this message we can break here - break; - } + var thisHandled = handler.Handle(typeIdentifier, topicFilter, this, receiveTime, originalData, result); + if (thisHandled) + processed = true; } if (!processed) @@ -1163,7 +1151,9 @@ namespace CryptoExchange.Net.Sockets.Default { var updatedList = new List(_listeners); updatedList.Add(processor); + _routeTable = new RouteTable(updatedList); _listeners = updatedList.AsReadOnly(); + } } @@ -1173,6 +1163,7 @@ namespace CryptoExchange.Net.Sockets.Default { var updatedList = new List(_listeners); updatedList.Remove(processor); + _routeTable = new RouteTable(updatedList); _listeners = updatedList.AsReadOnly(); } } @@ -1184,6 +1175,7 @@ namespace CryptoExchange.Net.Sockets.Default var updatedList = new List(_listeners); foreach (var processor in processors) updatedList.Remove(processor); + _routeTable = new RouteTable(updatedList); _listeners = updatedList.AsReadOnly(); } } diff --git a/CryptoExchange.Net/Sockets/Default/Subscription.cs b/CryptoExchange.Net/Sockets/Default/Subscription.cs index d3ad6e2..04dde72 100644 --- a/CryptoExchange.Net/Sockets/Default/Subscription.cs +++ b/CryptoExchange.Net/Sockets/Default/Subscription.cs @@ -180,7 +180,7 @@ namespace CryptoExchange.Net.Sockets.Default /// /// Handle an update message /// - public bool Handle(string? topicFilter, SocketConnection connection, DateTime receiveTime, string? originalData, object data, RouteMapEntry routeMap) + public bool Handle(string typeIdentifier, string? topicFilter, SocketConnection connection, DateTime receiveTime, string? originalData, object data) { ConnectionInvocations++; TotalInvocations++; @@ -193,7 +193,9 @@ namespace CryptoExchange.Net.Sockets.Default SubscriptionQuery.Timeout(); } - return routeMap.Handle(topicFilter, connection, receiveTime, originalData, data, out _); + return MessageRouter[typeIdentifier].Handle(topicFilter, connection, receiveTime, originalData, data, out _); + + //return routeMap.Handle(topicFilter, connection, receiveTime, originalData, data, out _); } /// diff --git a/CryptoExchange.Net/Sockets/Interfaces/IMessageProcessor.cs b/CryptoExchange.Net/Sockets/Interfaces/IMessageProcessor.cs index aa3ef27..2b59419 100644 --- a/CryptoExchange.Net/Sockets/Interfaces/IMessageProcessor.cs +++ b/CryptoExchange.Net/Sockets/Interfaces/IMessageProcessor.cs @@ -21,6 +21,7 @@ namespace CryptoExchange.Net.Sockets.Interfaces /// /// Handle a message /// - bool Handle(string? topicFilter, SocketConnection connection, DateTime receiveTime, string? originalData, object result, RouteMapEntry route); + //bool Handle(string? topicFilter, SocketConnection connection, DateTime receiveTime, string? originalData, object result, MessageRoute route); + bool Handle(string typeIdentifier, string? topicFilter, SocketConnection socketConnection, DateTime receiveTime, string? originalData, object result); } } diff --git a/CryptoExchange.Net/Sockets/MessageRouter.cs b/CryptoExchange.Net/Sockets/MessageRouter.cs index 54add04..23253e6 100644 --- a/CryptoExchange.Net/Sockets/MessageRouter.cs +++ b/CryptoExchange.Net/Sockets/MessageRouter.cs @@ -1,5 +1,6 @@ using CryptoExchange.Net.Objects; using CryptoExchange.Net.Sockets.Default; +using CryptoExchange.Net.Sockets.Interfaces; using System; #if NET8_0_OR_GREATER using System.Collections.Frozen; @@ -9,6 +10,44 @@ using System.Linq; namespace CryptoExchange.Net.Sockets { + public class RouteTable + { + private Dictionary _routeTableEntries; + + public RouteTable(IEnumerable processors) + { + _routeTableEntries = new Dictionary(); + foreach (var entry in processors) + { + foreach(var route in entry.MessageRouter.Routes) + { + if (!_routeTableEntries.ContainsKey(route.TypeIdentifier)) { + _routeTableEntries.Add(route.TypeIdentifier, new RouteTableEntry() + { + RouteType = route.DeserializationType, + Handlers = new List() + }); + } + + _routeTableEntries[route.TypeIdentifier].Handlers.Add(entry); + } + + } + } + + public RouteTableEntry? GetRouteTableEntry(string typeIdentifier) + { + return _routeTableEntries.TryGetValue(typeIdentifier, out var entry) ? entry : null; + } + } + + public record RouteTableEntry + { + public Type RouteType { get; set; } + public List Handlers { get; set; } + + } + public record RouteMapEntry { public Type RouteType { get; } diff --git a/CryptoExchange.Net/Sockets/Query.cs b/CryptoExchange.Net/Sockets/Query.cs index d67e497..ec283cb 100644 --- a/CryptoExchange.Net/Sockets/Query.cs +++ b/CryptoExchange.Net/Sockets/Query.cs @@ -164,7 +164,7 @@ namespace CryptoExchange.Net.Sockets /// /// Handle a response message /// - public abstract bool Handle(string? topicFilter, SocketConnection connection, DateTime receiveTime, string? originalData, object message, RouteMapEntry routeMap); + public abstract bool Handle(string typeIdentifier, string? topicFilter, SocketConnection connection, DateTime receiveTime, string? originalData, object message); } @@ -194,7 +194,7 @@ namespace CryptoExchange.Net.Sockets } /// - public override bool Handle(string? topicFilter, SocketConnection connection, DateTime receiveTime, string? originalData, object message, RouteMapEntry routeMap) + public override bool Handle(string typeIdentifier, string? topicFilter, SocketConnection connection, DateTime receiveTime, string? originalData, object message) { CurrentResponses++; if (CurrentResponses == RequiredResponses) @@ -203,7 +203,7 @@ namespace CryptoExchange.Net.Sockets if (Result?.Success != false) { // If an error result is already set don't override that - routeMap.Handle(topicFilter, connection, receiveTime, originalData, message, out var result); + MessageRouter[typeIdentifier].Handle(topicFilter, connection, receiveTime, originalData, message, out var result); Result = result; if (Result == null) // Null from Handle means it wasn't actually for this query