@@ -40,11 +40,56 @@ public bool TryGetHandler( HttpContext context, [NotNullWhen( true )] out ODataB
4040 return false ;
4141 }
4242
43+ var routeData = new RouteValueDictionary ( ) ;
44+ var candidates = new Dictionary < ApiVersion , int > ( capacity : mappings . Length ) ;
45+
46+ batchHandler = SelectExactMatch ( context , routeData , candidates ) ??
47+ SelectBestCandidate ( context , candidates , routeData ) ;
48+
49+ return batchHandler is not null ;
50+ }
51+
52+ public ValueTask < ODataBatchHandler ? > TryGetHandlerAsync ( HttpContext context , CancellationToken cancellationToken )
53+ {
54+ if ( count == 0 )
55+ {
56+ return ValueTask . FromResult ( default ( ODataBatchHandler ) ) ;
57+ }
58+
59+ var routeData = new RouteValueDictionary ( ) ;
60+ var candidates = new Dictionary < ApiVersion , int > ( capacity : mappings . Length ) ;
61+
62+ if ( SelectExactMatch ( context , routeData , candidates ) is { } handler )
63+ {
64+ return ValueTask . FromResult < ODataBatchHandler ? > ( handler ) ;
65+ }
66+
67+ return SelectBestCandidateAsync ( context , candidates , routeData , cancellationToken ) ;
68+ }
69+
70+ private static void MergeRouteData ( HttpContext context , RouteValueDictionary routeData )
71+ {
72+ if ( routeData . Count == 0 )
73+ {
74+ return ;
75+ }
76+
77+ var batchRouteData = context . ODataFeature ( ) . BatchRouteData ;
78+
79+ foreach ( var ( key , value ) in routeData )
80+ {
81+ batchRouteData . Add ( key , value ) ;
82+ }
83+ }
84+
85+ private ODataBatchHandler ? SelectExactMatch (
86+ HttpContext context ,
87+ RouteValueDictionary routeData ,
88+ Dictionary < ApiVersion , int > candidates )
89+ {
4390 var path = context . Request . Path ;
4491 var feature = context . ApiVersioningFeature ( ) ;
4592 var unspecified = feature . RawRequestedApiVersions . Count == 0 ;
46- var routeData = new RouteValueDictionary ( ) ;
47- var candidates = new Dictionary < ApiVersion , int > ( capacity : mappings . Length ) ;
4893
4994 for ( var i = 0 ; i < count ; i ++ )
5095 {
@@ -73,32 +118,39 @@ public bool TryGetHandler( HttpContext context, [NotNullWhen( true )] out ODataB
73118 }
74119
75120 MergeRouteData ( context , routeData ) ;
76- batchHandler = handler ;
77- return true ;
121+ return handler ;
78122 }
79123
80- batchHandler = SelectBestCandidate ( context , ref path , candidates , routeData ) ;
81- return batchHandler is not null ;
124+ return default ;
82125 }
83126
84- private static void MergeRouteData ( HttpContext context , RouteValueDictionary routeData )
127+ private ODataBatchHandler ? SelectBestCandidate (
128+ HttpContext context ,
129+ Dictionary < ApiVersion , int > candidates ,
130+ RouteValueDictionary routeData ,
131+ ApiVersion version )
85132 {
86- if ( routeData . Count == 0 )
133+ if ( version is null || ! candidates . TryGetValue ( version , out var index ) )
87134 {
88- return ;
135+ return default ;
89136 }
90137
91- var batchRouteData = context . ODataFeature ( ) . BatchRouteData ;
138+ ref readonly var mapping = ref mappings [ index ] ;
139+ var ( matcher , handler , _) = mapping ;
92140
93- foreach ( var ( key , value ) in routeData )
94- {
95- batchRouteData . Add ( key , value ) ;
96- }
141+ routeData . Clear ( ) ;
142+ matcher . TryMatch ( context . Request . Path , routeData ) ;
143+ MergeRouteData ( context , routeData ) ;
144+
145+ // it's important that the resolved api version be set here to ensure the correct
146+ // ODataOptions are resolved by ODataBatchHandler when executed
147+ context . ApiVersioningFeature ( ) . RequestedApiVersion = version ;
148+
149+ return handler ;
97150 }
98151
99152 private ODataBatchHandler ? SelectBestCandidate (
100153 HttpContext context ,
101- ref PathString path ,
102154 Dictionary < ApiVersion , int > candidates ,
103155 RouteValueDictionary routeData )
104156 {
@@ -114,22 +166,27 @@ private static void MergeRouteData( HttpContext context, RouteValueDictionary ro
114166 var model = new ApiVersionModel ( candidates . Keys , Enumerable . Empty < ApiVersion > ( ) ) ;
115167 var version = selector . SelectVersion ( context . Request , model ) ;
116168
117- if ( version is null || ! candidates . TryGetValue ( version , out var index ) )
169+ return SelectBestCandidate ( context , candidates , routeData , version ) ;
170+ }
171+
172+ private async ValueTask < ODataBatchHandler ? > SelectBestCandidateAsync (
173+ HttpContext context ,
174+ Dictionary < ApiVersion , int > candidates ,
175+ RouteValueDictionary routeData ,
176+ CancellationToken cancellationToken )
177+ {
178+ if ( candidates . Count == 0 )
118179 {
119180 return default ;
120181 }
121182
122- ref readonly var mapping = ref mappings [ index ] ;
123- var ( matcher , handler , _) = mapping ;
124-
125- routeData . Clear ( ) ;
126- matcher . TryMatch ( path , routeData ) ;
127- MergeRouteData ( context , routeData ) ;
128-
129- // it's important that the resolved api version be set here to ensure the correct
130- // ODataOptions are resolved by ODataBatchHandler when executed
131- context . ApiVersioningFeature ( ) . RequestedApiVersion = version ;
183+ // ~/$batch is always version-neutral so there is no need to check
184+ // ApiVersioningOptions.AllowDefaultVersionWhenUnspecified. use the
185+ // configured IApiVersionSelector to provide a chance to select the
186+ // most appropriate version.
187+ var model = new ApiVersionModel ( candidates . Keys , Enumerable . Empty < ApiVersion > ( ) ) ;
188+ var version = await selector . SelectVersionAsync ( context . Request , model , cancellationToken ) . ConfigureAwait ( false ) ;
132189
133- return handler ;
190+ return SelectBestCandidate ( context , candidates , routeData , version ) ;
134191 }
135192}
0 commit comments