@@ -49,6 +49,17 @@ public MessageBusBase(TOptions options)
4949 /// </summary>
5050 protected CancellationToken DisposedCancellationToken => _disposedCancellationTokenSource . Token ;
5151
52+ /// <summary>
53+ /// Creates a linked cancellation token source that combines the provided token with the disposal token.
54+ /// This allows operations to be cancelled by either the caller or when this instance is disposed.
55+ /// </summary>
56+ /// <param name="cancellationToken">The caller's cancellation token to link.</param>
57+ /// <returns>A new <see cref="CancellationTokenSource"/> that should be disposed by the caller.</returns>
58+ protected CancellationTokenSource GetLinkedDisposableCancellationTokenSource ( CancellationToken cancellationToken )
59+ {
60+ return CancellationTokenSource . CreateLinkedTokenSource ( cancellationToken , DisposedCancellationToken ) ;
61+ }
62+
5263 ILogger IHaveLogger . Logger => _logger ;
5364 ILoggerFactory IHaveLoggerFactory . LoggerFactory => _loggerFactory ;
5465 TimeProvider IHaveTimeProvider . TimeProvider => _timeProvider ;
@@ -73,10 +84,11 @@ public async Task PublishAsync(Type messageType, object message, MessageOptions
7384 options . Properties . Add ( "TraceState" , Activity . Current . TraceStateString ) ;
7485 }
7586
87+ using var linkedCancellationTokenSource = GetLinkedDisposableCancellationTokenSource ( cancellationToken ) ;
7688 try
7789 {
78- await EnsureTopicCreatedAsync ( cancellationToken ) . AnyContext ( ) ;
79- await PublishImplAsync ( GetMappedMessageType ( messageType ) , message , options , cancellationToken ) . AnyContext ( ) ;
90+ await EnsureTopicCreatedAsync ( linkedCancellationTokenSource . Token ) . AnyContext ( ) ;
91+ await PublishImplAsync ( GetMappedMessageType ( messageType ) , message , options , linkedCancellationTokenSource . Token ) . AnyContext ( ) ;
8092 }
8193 catch ( Exception ex ) when ( ex is not OperationCanceledException and not MessageBusException )
8294 {
@@ -105,8 +117,8 @@ protected virtual Type GetMappedMessageType(string messageType)
105117
106118 return _knownMessageTypesCache . GetOrAdd ( messageType , type =>
107119 {
108- if ( _options . MessageTypeMappings != null && _options . MessageTypeMappings . ContainsKey ( type ) )
109- return _options . MessageTypeMappings [ type ] ;
120+ if ( _options . MessageTypeMappings != null && _options . MessageTypeMappings . TryGetValue ( type , out Type typeMapping ) )
121+ return typeMapping ;
110122
111123 try
112124 {
@@ -126,7 +138,6 @@ protected virtual Type GetMappedMessageType(string messageType)
126138 catch ( Exception ex )
127139 {
128140 _logger . LogError ( ex , "Error getting message body type: {MessageType}" , type ) ;
129-
130141 return null ;
131142 }
132143 }
@@ -145,7 +156,7 @@ protected virtual Task SubscribeImplAsync<T>(Func<T, CancellationToken, Task> ha
145156 Action = ( message , token ) =>
146157 {
147158 if ( message is T typedMessage )
148- return handler ( typedMessage , cancellationToken ) ;
159+ return handler ( typedMessage , token ) ;
149160
150161 _logger . LogTrace ( "Unable to call subscriber action: {MessageType} cannot be safely casted to {SubscriberType}" , message . GetType ( ) , typeof ( T ) ) ;
151162 return Task . CompletedTask ;
@@ -157,11 +168,11 @@ protected virtual Task SubscribeImplAsync<T>(Func<T, CancellationToken, Task> ha
157168 cancellationToken . Register ( ( ) =>
158169 {
159170 _subscribers . TryRemove ( subscriber . Id , out _ ) ;
160- if ( _subscribers . Count == 0 )
161- {
162- _logger . LogDebug ( "Removing topic subscription for {MessageBusId}: No subscribers" , MessageBusId ) ;
163- RemoveTopicSubscriptionAsync ( ) . GetAwaiter ( ) . GetResult ( ) ;
164- }
171+ if ( _subscribers . Count > 0 )
172+ return ;
173+
174+ _logger . LogDebug ( "Removing topic subscription for {MessageBusId}: No subscribers" , MessageBusId ) ;
175+ RemoveTopicSubscriptionAsync ( ) . GetAwaiter ( ) . GetResult ( ) ;
165176 } ) ;
166177 }
167178
@@ -265,7 +276,7 @@ protected async Task SendMessageToSubscribersAsync(IMessage message)
265276
266277 return Task . Run ( async ( ) =>
267278 {
268- if ( subscriber . CancellationToken . IsCancellationRequested )
279+ if ( DisposedCancellationToken . IsCancellationRequested || subscriber . CancellationToken . IsCancellationRequested )
269280 {
270281 _logger . LogTrace ( "The cancelled subscriber action will not be called: {SubscriberId}" , subscriber . Id ) ;
271282 return ;
@@ -422,14 +433,6 @@ public virtual void Dispose()
422433 _disposedCancellationTokenSource . Dispose ( ) ;
423434 }
424435
425- [ DebuggerDisplay ( "MessageType: {MessageType} SendTime: {SendTime} Message: {Message}" ) ]
426- protected class DelayedMessage
427- {
428- public DateTime SendTime { get ; set ; }
429- public Type MessageType { get ; set ; }
430- public object Message { get ; set ; }
431- }
432-
433436 [ DebuggerDisplay ( "Id: {Id} Type: {Type} CancellationToken: {CancellationToken}" ) ]
434437 protected class Subscriber
435438 {
0 commit comments