@@ -36,7 +36,7 @@ private static class GenericDispatcherCache<TRes>
3636
3737 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
3838 public ValueTask < TResponse > Send < TRequest , TResponse > ( TRequest request , string name = null , CancellationToken ct = default )
39- where TRequest : notnull
39+ where TRequest : notnull , IRequest < TResponse >
4040 where TResponse : notnull
4141 {
4242 if ( ct . IsCancellationRequested )
@@ -172,18 +172,67 @@ static async ValueTask<TResponse> AwaitLite(ValueTask<TResponse> task, IServiceS
172172 }
173173 }
174174
175- // Constrained overloads to avoid boxing for IRequest<TResponse>
176175 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
177- public ValueTask < TResponse > Send < TRequest , TResponse > ( in TRequest request , CancellationToken ct = default )
178- where TRequest : struct , IRequest < TResponse >
176+ public ValueTask < TResponse > Send < TResponse > ( IRequest < TResponse > request , string name = null , CancellationToken ct = default )
179177 where TResponse : notnull
180- => Send < TRequest , TResponse > ( request , null , ct ) ;
178+ {
179+ if ( ct . IsCancellationRequested )
180+ return ValueTask . FromCanceled < TResponse > ( ct ) ;
181181
182- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
183- public ValueTask < TResponse > Send < TRequest , TResponse > ( TRequest request , CancellationToken ct = default )
184- where TRequest : class , IRequest < TResponse >
185- where TResponse : notnull
186- => Send < TRequest , TResponse > ( request , null , ct ) ;
182+ if ( IsFastPath ( spaceRegistry . HandlerLifetime ) )
183+ {
184+ // Try runtime-type lookup without expression/MakeGenericMethod
185+ if ( spaceRegistry . TryGetHandlerEntryByRuntimeType ( request . GetType ( ) , typeof ( TResponse ) , name , out var entryObj )
186+ && entryObj is SpaceRegistry . IObjectHandlerEntry entry )
187+ {
188+ var hctx = HandlerContextStruct . Create ( rootProvider , request , this , ct ) ;
189+ var vto = entry . InvokeObject ( hctx ) ;
190+
191+ if ( vto . IsCompletedSuccessfully )
192+ return new ValueTask < TResponse > ( ( TResponse ) vto . Result ! ) ;
193+
194+ return AwaitFast1 ( vto ) ;
195+
196+ static async ValueTask < TResponse > AwaitFast1 ( ValueTask < object > vt )
197+ => ( TResponse ) await vt . ConfigureAwait ( false ) ;
198+ }
199+
200+ // Fallback: object dispatch through registry (still no expression compile)
201+ var vtoFallback = spaceRegistry . DispatchHandler ( request , name , typeof ( TResponse ) , rootProvider , ct ) ;
202+
203+ if ( vtoFallback . IsCompletedSuccessfully )
204+ return new ValueTask < TResponse > ( ( TResponse ) vtoFallback . Result ! ) ;
205+
206+ return AwaitFast2 ( vtoFallback ) ;
207+
208+ static async ValueTask < TResponse > AwaitFast2 ( ValueTask < object > vt )
209+ => ( TResponse ) await vt . ConfigureAwait ( false ) ;
210+ }
211+
212+ // Scoped path
213+ using var scope = scopeFactory . CreateScope ( ) ;
214+ if ( ct . IsCancellationRequested )
215+ return ValueTask . FromCanceled < TResponse > ( ct ) ;
216+
217+ var vts = spaceRegistry . DispatchHandler ( request , name , typeof ( TResponse ) , scope . ServiceProvider , ct ) ;
218+
219+ if ( vts . IsCompletedSuccessfully )
220+ return new ValueTask < TResponse > ( ( TResponse ) vts . Result ! ) ;
221+
222+ return AwaitScoped ( vts , scope ) ;
223+
224+ static async ValueTask < TResponse > AwaitScoped ( ValueTask < object > vt , IServiceScope scope )
225+ {
226+ try
227+ {
228+ return ( TResponse ) await vt . ConfigureAwait ( false ) ;
229+ }
230+ finally
231+ {
232+ scope . Dispose ( ) ;
233+ }
234+ }
235+ }
187236
188237 #endregion
189238
@@ -311,4 +360,5 @@ async ValueTask SlowPublishScoped(TRequest req, NotificationDispatchType dt, Can
311360 }
312361 }
313362
363+
314364}
0 commit comments