@@ -94,7 +94,6 @@ void AppendEntitySetOrOperation( IList<string> segments )
9494#else
9595 var controllerDescriptor = Context . ActionDescriptor ;
9696#endif
97- var controllerName = controllerDescriptor . ControllerName ;
9897
9998 if ( Context . IsAttributeRouted )
10099 {
@@ -103,32 +102,47 @@ void AppendEntitySetOrOperation( IList<string> segments )
103102#else
104103 var prefix = controllerDescriptor . ControllerTypeInfo . GetCustomAttributes < ODataRoutePrefixAttribute > ( ) . FirstOrDefault ( ) ? . Prefix ? . Trim ( '/' ) ;
105104#endif
106- var template = Context . RouteTemplate ;
105+ AppendEntitySetOrOperationFromAttributes ( segments , prefix ) ;
106+ }
107+ else
108+ {
109+ AppendEntitySetOrOperationFromConvention ( segments , controllerDescriptor . ControllerName ) ;
110+ }
111+ }
112+
113+ void AppendEntitySetOrOperationFromAttributes ( IList < string > segments , string prefix )
114+ {
115+ var template = Context . RouteTemplate ;
107116
108- if ( IsNullOrEmpty ( prefix ) )
117+ if ( Context . IsOperation && Context . RouteTemplateGeneration == Client )
118+ {
119+ template = FixUpArrayParameters ( template , Context . Operation ) ;
120+ }
121+
122+ if ( IsNullOrEmpty ( prefix ) )
123+ {
124+ segments . Add ( template ) ;
125+ }
126+ else
127+ {
128+ if ( IsNullOrEmpty ( template ) )
129+ {
130+ segments . Add ( prefix ) ;
131+ }
132+ else if ( template [ 0 ] == '(' && Context . UrlKeyDelimiter == Parentheses )
109133 {
110- segments . Add ( Context . RouteTemplate ) ;
134+ segments . Add ( prefix + template ) ;
111135 }
112136 else
113137 {
114- if ( IsNullOrEmpty ( template ) )
115- {
116- segments . Add ( prefix ) ;
117- }
118- else if ( template [ 0 ] == '(' && Context . UrlKeyDelimiter == Parentheses )
119- {
120- segments . Add ( prefix + template ) ;
121- }
122- else
123- {
124- segments . Add ( prefix ) ;
125- segments . Add ( template ) ;
126- }
138+ segments . Add ( prefix ) ;
139+ segments . Add ( template ) ;
127140 }
128-
129- return ;
130141 }
142+ }
131143
144+ void AppendEntitySetOrOperationFromConvention ( IList < string > segments , string controllerName )
145+ {
132146 var builder = new StringBuilder ( ) ;
133147
134148 switch ( Context . ActionType )
@@ -244,33 +258,23 @@ void AppendParametersFromConvention( StringBuilder builder, IEdmOperation operat
244258 var actionParameters = Context . ParameterDescriptions . ToDictionary ( p => p . Name , StringComparer . OrdinalIgnoreCase ) ;
245259 var parameter = parameters . Current ;
246260 var name = parameter . Name ;
247- #if WEBAPI
248- var routeParameterName = actionParameters [ name ] . ParameterDescriptor . ParameterName ;
249- #elif API_EXPLORER
250- var routeParameterName = actionParameters [ name ] . ParameterDescriptor . Name ;
251- #else
252- var routeParameterName = actionParameters [ name ] . Name ;
253- #endif
261+ var routeParameterName = GetRouteParameterName ( actionParameters , name ) ;
254262
255263 builder . Append ( '(' ) ;
256264 builder . Append ( name ) ;
257265 builder . Append ( '=' ) ;
266+
258267 ExpandParameterTemplate ( builder , parameter , routeParameterName ) ;
259268
260269 while ( parameters . MoveNext ( ) )
261270 {
262271 parameter = parameters . Current ;
263272 name = parameter . Name ;
264- #if WEBAPI
265- routeParameterName = actionParameters [ name ] . ParameterDescriptor . ParameterName ;
266- #elif API_EXPLORER
267- routeParameterName = actionParameters [ name ] . ParameterDescriptor . Name ;
268- #else
269- routeParameterName = actionParameters [ name ] . Name ;
270- #endif
273+ routeParameterName = GetRouteParameterName ( actionParameters , name ) ;
271274 builder . Append ( ',' ) ;
272275 builder . Append ( name ) ;
273276 builder . Append ( '=' ) ;
277+
274278 ExpandParameterTemplate ( builder , parameter , routeParameterName ) ;
275279 }
276280
@@ -305,29 +309,128 @@ void ExpandParameterTemplate( StringBuilder template, IEdmTypeReference typeRefe
305309 return ;
306310 }
307311
308- if ( typeDef . TypeKind == EdmTypeKind . Enum )
312+ switch ( typeDef . TypeKind )
313+ {
314+ case EdmTypeKind . Collection :
315+ template . Insert ( offset , '[' ) ;
316+ template . Append ( ']' ) ;
317+ break ;
318+ case EdmTypeKind . Enum :
319+ var fullName = typeReference . FullName ( ) ;
320+
321+ if ( ! Context . AllowUnqualifiedEnum )
322+ {
323+ template . Insert ( offset , fullName ) ;
324+ offset += fullName . Length ;
325+ }
326+
327+ template . Insert ( offset , '\' ' ) ;
328+ template . Append ( '\' ' ) ;
329+ break ;
330+ default :
331+ var type = typeDef . GetClrType ( Context . EdmModel ) ;
332+
333+ if ( quotedTypes . TryGetValue ( type , out var prefix ) )
334+ {
335+ template . Insert ( offset , prefix ) ;
336+ offset += prefix . Length ;
337+ template . Insert ( offset , '\' ' ) ;
338+ template . Append ( '\' ' ) ;
339+ }
340+
341+ break ;
342+ }
343+ }
344+
345+ string FixUpArrayParameters ( string template , IEdmOperation operation )
346+ {
347+ Contract . Requires ( ! IsNullOrEmpty ( template ) ) ;
348+ Contract . Requires ( operation != null ) ;
349+
350+ if ( ! operation . IsFunction ( ) )
309351 {
310- var fullName = typeReference . FullName ( ) ;
352+ return template ;
353+ }
311354
312- if ( ! Context . AllowUnqualifiedEnum )
355+ int IndexOfToken ( StringBuilder builder , string token )
356+ {
357+ var index = - 1 ;
358+
359+ for ( var i = 0 ; i < builder . Length ; i ++ )
313360 {
314- template . Insert ( offset , fullName ) ;
315- offset += fullName . Length ;
361+ if ( builder [ i ] != '{' )
362+ {
363+ continue ;
364+ }
365+
366+ index = i ;
367+ ++ i ;
368+
369+ var matched = true ;
370+
371+ for ( var j = 0 ; j < token . Length ; i ++ , j ++ )
372+ {
373+ if ( builder [ i ] != token [ j ] )
374+ {
375+ matched = false ;
376+ break ;
377+ }
378+ }
379+
380+ if ( matched )
381+ {
382+ break ;
383+ }
384+
385+ while ( builder [ i ] != '}' )
386+ {
387+ ++ i ;
388+ }
316389 }
317390
318- template . Insert ( offset , '\' ' ) ;
319- template . Append ( '\' ' ) ;
320- return ;
391+ return index ;
321392 }
322393
323- var type = typeDef . GetClrType ( Context . EdmModel ) ;
394+ void InsertBrackets ( StringBuilder builder , string token )
395+ {
396+ var index = IndexOfToken ( builder , token ) ;
324397
325- if ( quotedTypes . TryGetValue ( type , out var prefix ) )
398+ if ( index >= 0 )
399+ {
400+ builder . Insert ( index , '[' ) . Insert ( index + token . Length + 3 , ']' ) ;
401+ }
402+ }
403+
404+ var collectionParameters = from param in operation . Parameters
405+ where param . Type . TypeKind ( ) == EdmTypeKind . Collection &&
406+ param . Name != "bindingParameter"
407+ select param ;
408+
409+ using ( var parameters = collectionParameters . GetEnumerator ( ) )
326410 {
327- template . Insert ( offset , prefix ) ;
328- offset += prefix . Length ;
329- template . Insert ( offset , '\' ' ) ;
330- template . Append ( '\' ' ) ;
411+ if ( ! parameters . MoveNext ( ) )
412+ {
413+ return template ;
414+ }
415+
416+ var buffer = new StringBuilder ( template ) ;
417+ var actionParameters = Context . ParameterDescriptions . ToDictionary ( p => p . Name , StringComparer . OrdinalIgnoreCase ) ;
418+ var parameter = parameters . Current ;
419+ var name = parameter . Name ;
420+ var routeParameterName = GetRouteParameterName ( actionParameters , name ) ;
421+
422+ InsertBrackets ( buffer , routeParameterName ) ;
423+
424+ while ( parameters . MoveNext ( ) )
425+ {
426+ parameter = parameters . Current ;
427+ name = parameter . Name ;
428+ routeParameterName = GetRouteParameterName ( actionParameters , name ) ;
429+
430+ InsertBrackets ( buffer , routeParameterName ) ;
431+ }
432+
433+ return buffer . ToString ( ) ;
331434 }
332435 }
333436
@@ -416,6 +519,17 @@ IList<ApiParameterDescription> GetQueryParameters( IList<ApiParameterDescription
416519 return queryParameters ;
417520 }
418521
522+ static string GetRouteParameterName ( IReadOnlyDictionary < string , ApiParameterDescription > actionParameters , string name )
523+ {
524+ #if WEBAPI
525+ return actionParameters [ name ] . ParameterDescriptor . ParameterName ;
526+ #elif API_EXPLORER
527+ return actionParameters [ name ] . ParameterDescriptor . Name ;
528+ #else
529+ return actionParameters [ name ] . Name ;
530+ #endif
531+ }
532+
419533 static bool IsBuiltInParameter ( Type parameterType ) => ODataQueryOptionsType . IsAssignableFrom ( parameterType ) || ODataActionParametersType . IsAssignableFrom ( parameterType ) ;
420534
421535 static bool IsKey ( IReadOnlyList < IEdmStructuralProperty > keys , ApiParameterDescription parameter )
0 commit comments