22// SPDX-License-Identifier: Apache-2.0
33
44using System . Collections . Concurrent ;
5+ using System . Diagnostics . CodeAnalysis ;
6+ using System . Linq . Expressions ;
7+ using System . Reflection ;
58using AWS . Messaging . Configuration . Internal ;
69using AWS . Messaging . Publishers ;
710using AWS . Messaging . Publishers . EventBridge ;
1215using AWS . Messaging . Services . Backoff ;
1316using AWS . Messaging . Services . Backoff . Policies ;
1417using AWS . Messaging . Telemetry ;
18+ using Microsoft . Extensions . Configuration ;
1519using Microsoft . Extensions . DependencyInjection ;
1620using Microsoft . Extensions . DependencyInjection . Extensions ;
17- using Microsoft . Extensions . Configuration ;
1821using Microsoft . Extensions . Logging ;
1922using Microsoft . Extensions . Logging . Abstractions ;
20- using System . Diagnostics . CodeAnalysis ;
21- using System . Reflection ;
2223
2324namespace AWS . Messaging . Configuration ;
2425
@@ -104,13 +105,20 @@ private IMessageBusBuilder AddPublisher([DynamicallyAccessedMembers(DynamicallyA
104105 return this ;
105106 }
106107
107- private IMessageBusBuilder AddMessageHandler ( [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . All ) ] Type handlerType , Type messageType , Func < MessageEnvelope > envelopeFactory , MethodInfo middlewareInvokeAsyncMethodInfo , string ? messageTypeIdentifier = null )
108+ private IMessageBusBuilder AddMessageHandler ( [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . All ) ] Type handlerType , Type messageType , Func < MessageEnvelope > envelopeFactory , HandlerInvokerDelegate handlerInvokerDelegate , string ? messageTypeIdentifier = null )
108109 {
109- var subscriberMapping = new SubscriberMapping ( handlerType , messageType , envelopeFactory , middlewareInvokeAsyncMethodInfo , messageTypeIdentifier ) ;
110+ var subscriberMapping = new SubscriberMapping ( handlerType , messageType , envelopeFactory , handlerInvokerDelegate , messageTypeIdentifier ) ;
110111 _messageConfiguration . SubscriberMappings . Add ( subscriberMapping ) ;
111112 return this ;
112113 }
113114
115+ public IMessageBusBuilder AddMessageErrorHandler < [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors ) ] T > ( ServiceLifetime serviceLifetime = ServiceLifetime . Singleton )
116+ where T : IMessageErrorHandler
117+ {
118+ AddAdditionalService ( new ServiceDescriptor ( typeof ( IMessageErrorHandler ) , typeof ( T ) , serviceLifetime ) ) ;
119+ return this ;
120+ }
121+
114122 public IMessageBusBuilder AddMiddleware < [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . All ) ] TMiddleware > ( ServiceLifetime serviceLifetime = ServiceLifetime . Singleton )
115123 where TMiddleware : class , IMiddleware
116124 {
@@ -226,24 +234,20 @@ public IMessageBusBuilder LoadConfigurationFromSettings(IConfiguration configura
226234
227235 if ( settings . MessageHandlers != null )
228236 {
229- // This is not Native AOT compatible but the method in general is marked
230- // as not being Native AOT compatible due to loading dynamic types. So this
231- // not being Native AOT compatible is okay.
232- var middlewareInvokeMethod = typeof ( IMiddleware ) . GetMethod ( nameof ( IMiddleware . InvokeAsync ) ) ! ;
233-
234237 foreach ( var messageHandler in settings . MessageHandlers )
235238 {
236239 var messageType = GetTypeFromAssemblies ( callingAssembly , messageHandler . MessageType )
237240 ?? throw new InvalidAppSettingsConfigurationException ( $ "Unable to find the provided message type '{ messageHandler . MessageType } '.") ;
238241 var handlerType = GetTypeFromAssemblies ( callingAssembly , messageHandler . HandlerType )
239242 ?? throw new InvalidAppSettingsConfigurationException ( $ "Unable to find the provided message handler type '{ messageHandler . HandlerType } '.") ;
240243
244+ var messageEnvelopeType = typeof ( MessageEnvelope < > ) . MakeGenericType ( messageType ) ;
245+
241246 // This func is not Native AOT compatible but the method in general is marked
242247 // as not being Native AOT compatible due to loading dynamic types. So this
243248 // func not being Native AOT compatible is okay.
244249 MessageEnvelope envelopeFactory ( )
245250 {
246- var messageEnvelopeType = typeof ( MessageEnvelope < > ) . MakeGenericType ( messageType ) ;
247251 var envelope = Activator . CreateInstance ( messageEnvelopeType ) ;
248252 if ( envelope == null || envelope is not MessageEnvelope )
249253 {
@@ -253,12 +257,8 @@ MessageEnvelope envelopeFactory()
253257 return ( MessageEnvelope ) envelope ;
254258 }
255259
256- // MakeGenericMethod is not Native AOT compatible but the method in general is marked
257- // as not being Native AOT compatible due to loading dynamic types. So this
258- // method not being Native AOT compatible is okay.
259- var middlewareInvokeAsyncMethodInfo = middlewareInvokeMethod . MakeGenericMethod ( messageType ) ;
260-
261- AddMessageHandler ( handlerType , messageType , envelopeFactory , middlewareInvokeAsyncMethodInfo , messageHandler . MessageTypeIdentifier ) ;
260+ var handlerInoker = BuildHandlerInvoker ( messageType , messageEnvelopeType ) ;
261+ AddMessageHandler ( handlerType , messageType , envelopeFactory , handlerInoker , messageHandler . MessageTypeIdentifier ) ;
262262 }
263263 }
264264
@@ -303,6 +303,29 @@ MessageEnvelope envelopeFactory()
303303 }
304304
305305 return this ;
306+
307+ // This is not Native AOT compatible but the method in general is marked
308+ // as not being Native AOT compatible due to loading dynamic types. So this
309+ // func not being Native AOT compatible is okay.
310+ static HandlerInvokerDelegate BuildHandlerInvoker ( Type messageType , Type messageEnvelopeType )
311+ {
312+ var invokerParam = Expression . Parameter ( typeof ( HandlerInvoker ) , "invoker" ) ;
313+ var envelopeParam = Expression . Parameter ( typeof ( MessageEnvelope ) , "envelope" ) ;
314+ var mappingParam = Expression . Parameter ( typeof ( SubscriberMapping ) , "mapping" ) ;
315+ var tokenParam = Expression . Parameter ( typeof ( CancellationToken ) , "token" ) ;
316+
317+ // invoker.InvokeAsync<T>( (MessageEnvelope<T>) envelope, mapping, token )
318+ var genericMethodDef = typeof ( HandlerInvoker )
319+ . GetMethods ( BindingFlags . Public | BindingFlags . Instance )
320+ . First ( m => m . Name == nameof ( HandlerInvoker . InvokeAsync ) && m . IsGenericMethodDefinition )
321+ . GetGenericMethodDefinition ( ) ;
322+
323+ var closedMethod = genericMethodDef . MakeGenericMethod ( messageType ) ;
324+ var typedEnvelope = Expression . Convert ( envelopeParam , messageEnvelopeType ) ;
325+ var call = Expression . Call ( invokerParam , closedMethod , typedEnvelope , mappingParam , tokenParam ) ;
326+ var lambda = Expression . Lambda < HandlerInvokerDelegate > ( call , invokerParam , envelopeParam , mappingParam , tokenParam ) ;
327+ return lambda . Compile ( ) ;
328+ }
306329 }
307330
308331 [ RequiresUnreferencedCode ( "This method requires loading types dynamically as defined in the configuration system." ) ]
0 commit comments