diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
index 5e9fd87f3..390df02a6 100644
--- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
+++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
@@ -1102,20 +1102,6 @@
The type to test.
True if the type is a DateTime; false otherwise.
-
-
- Determine if a type is a .
-
- The type to test.
- True if the type is a DateOnly; false otherwise.
-
-
-
- Determine if a type is a .
-
- The type to test.
- True if the type is a TimeOnly; false otherwise.
-
Determine if a type is a TimeSpan.
@@ -13000,6 +12986,14 @@
+
+
+ Tests whether to apply $count on the .
+
+ The entity set to test.
+ The route options.
+ True/false to identify whether to apply $count.
+
The convention for .
@@ -13108,6 +13102,16 @@
The declaring name (out).
The http method name or null.
+
+
+ OData spec: To request only the number of items of a collection of entities or items of a collection-valued property,
+ the client issues a GET request with /$count appended to the resource path of the collection.
+
+ The property to test.
+ The http method.
+ The route options.
+ True/false to identify whether to apply $count.
+
A context object for .
@@ -13280,6 +13284,35 @@
+
+
+ Tests whether to apply routings for the given property.
+
+ The property to test.
+ The http method.
+ The route options.
+ True/false to identify whether to apply routings for the given property.
+
+
+
+ OData spec: To retrieve the raw value of a primitive type property, the client sends a GET request to the property value URL.
+ So, let's apply $value for the "Get" and non-collection primitive property
+
+ The property to test.
+ The http method.
+ The route options.
+ True/false to identify whether to apply $value.
+
+
+
+ OData spec: To request only the number of items of a collection of entities or items of a collection-valued property,
+ the client issues a GET request with /$count appended to the resource path of the collection.
+
+ The property to test.
+ The http method.
+ The route options.
+ True/false to identify whether to apply $count.
+
An implementation of that handles entity reference manipulations.
@@ -13656,6 +13689,16 @@
Initializes a new instance of the class.
+
+
+ Gets/sets a value indicating whether to enable $count in conventional routing.
+
+
+
+
+ Gets/sets a value indicating whether to enable $value in conventional routing.
+
+
Gets/sets a value indicating whether to enable case insensitive for the action name in conventional routing.
@@ -14933,3 +14976,19 @@
+ummary>
+ The value segment.
+
+
+
+ Gets the value segment.
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt
index 699ca163f..96497723a 100644
--- a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt
+++ b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt
@@ -1331,6 +1331,12 @@ Microsoft.AspNetCore.OData.Routing.ODataRouteOptions.EnableActionNameCaseInsensi
Microsoft.AspNetCore.OData.Routing.ODataRouteOptions.EnableActionNameCaseInsensitive.set -> void
Microsoft.AspNetCore.OData.Routing.ODataRouteOptions.EnableControllerNameCaseInsensitive.get -> bool
Microsoft.AspNetCore.OData.Routing.ODataRouteOptions.EnableControllerNameCaseInsensitive.set -> void
+Microsoft.AspNetCore.OData.Routing.ODataRouteOptions.EnableConventionalRoutingIfODataRoutingPresent.get -> bool
+Microsoft.AspNetCore.OData.Routing.ODataRouteOptions.EnableConventionalRoutingIfODataRoutingPresent.set -> void
+Microsoft.AspNetCore.OData.Routing.ODataRouteOptions.EnableDollarCountRouting.get -> bool
+Microsoft.AspNetCore.OData.Routing.ODataRouteOptions.EnableDollarCountRouting.set -> void
+Microsoft.AspNetCore.OData.Routing.ODataRouteOptions.EnableDollarValueRouting.get -> bool
+Microsoft.AspNetCore.OData.Routing.ODataRouteOptions.EnableDollarValueRouting.set -> void
Microsoft.AspNetCore.OData.Routing.ODataRouteOptions.EnableKeyAsSegment.get -> bool
Microsoft.AspNetCore.OData.Routing.ODataRouteOptions.EnableKeyAsSegment.set -> void
Microsoft.AspNetCore.OData.Routing.ODataRouteOptions.EnableKeyInParenthesis.get -> bool
@@ -1978,12 +1984,14 @@ virtual Microsoft.AspNetCore.OData.Routing.Conventions.EntityRoutingConvention.A
virtual Microsoft.AspNetCore.OData.Routing.Conventions.EntityRoutingConvention.Order.get -> int
virtual Microsoft.AspNetCore.OData.Routing.Conventions.EntitySetRoutingConvention.AppliesToAction(Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context) -> bool
virtual Microsoft.AspNetCore.OData.Routing.Conventions.EntitySetRoutingConvention.AppliesToController(Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context) -> bool
+virtual Microsoft.AspNetCore.OData.Routing.Conventions.EntitySetRoutingConvention.CanApplyDollarCount(Microsoft.OData.Edm.IEdmEntitySet entitySet, Microsoft.AspNetCore.OData.Routing.ODataRouteOptions routeOptions) -> bool
virtual Microsoft.AspNetCore.OData.Routing.Conventions.EntitySetRoutingConvention.Order.get -> int
virtual Microsoft.AspNetCore.OData.Routing.Conventions.MetadataRoutingConvention.AppliesToAction(Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context) -> bool
virtual Microsoft.AspNetCore.OData.Routing.Conventions.MetadataRoutingConvention.AppliesToController(Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context) -> bool
virtual Microsoft.AspNetCore.OData.Routing.Conventions.MetadataRoutingConvention.Order.get -> int
virtual Microsoft.AspNetCore.OData.Routing.Conventions.NavigationRoutingConvention.AppliesToAction(Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context) -> bool
virtual Microsoft.AspNetCore.OData.Routing.Conventions.NavigationRoutingConvention.AppliesToController(Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context) -> bool
+virtual Microsoft.AspNetCore.OData.Routing.Conventions.NavigationRoutingConvention.CanApplyDollarCount(Microsoft.OData.Edm.IEdmNavigationProperty edmProperty, string method, Microsoft.AspNetCore.OData.Routing.ODataRouteOptions routeOptions) -> bool
virtual Microsoft.AspNetCore.OData.Routing.Conventions.NavigationRoutingConvention.Order.get -> int
virtual Microsoft.AspNetCore.OData.Routing.Conventions.OperationImportRoutingConvention.AppliesToAction(Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context) -> bool
virtual Microsoft.AspNetCore.OData.Routing.Conventions.OperationImportRoutingConvention.AppliesToController(Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context) -> bool
@@ -1991,6 +1999,9 @@ virtual Microsoft.AspNetCore.OData.Routing.Conventions.OperationImportRoutingCon
virtual Microsoft.AspNetCore.OData.Routing.Conventions.OperationRoutingConvention.AppliesToController(Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context) -> bool
virtual Microsoft.AspNetCore.OData.Routing.Conventions.PropertyRoutingConvention.AppliesToAction(Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context) -> bool
virtual Microsoft.AspNetCore.OData.Routing.Conventions.PropertyRoutingConvention.AppliesToController(Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context) -> bool
+virtual Microsoft.AspNetCore.OData.Routing.Conventions.PropertyRoutingConvention.CanApply(Microsoft.OData.Edm.IEdmProperty edmProperty, string method, Microsoft.AspNetCore.OData.Routing.ODataRouteOptions routeOptions) -> bool
+virtual Microsoft.AspNetCore.OData.Routing.Conventions.PropertyRoutingConvention.CanApplyDollarCount(Microsoft.OData.Edm.IEdmProperty edmProperty, string method, Microsoft.AspNetCore.OData.Routing.ODataRouteOptions routeOptions) -> bool
+virtual Microsoft.AspNetCore.OData.Routing.Conventions.PropertyRoutingConvention.CanApplyDollarValue(Microsoft.OData.Edm.IEdmProperty edmProperty, string method, Microsoft.AspNetCore.OData.Routing.ODataRouteOptions routeOptions) -> bool
virtual Microsoft.AspNetCore.OData.Routing.Conventions.PropertyRoutingConvention.Order.get -> int
virtual Microsoft.AspNetCore.OData.Routing.Conventions.RefRoutingConvention.AppliesToController(Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context) -> bool
virtual Microsoft.AspNetCore.OData.Routing.Conventions.RefRoutingConvention.Order.get -> int
diff --git a/src/Microsoft.AspNetCore.OData/Routing/Conventions/EntitySetRoutingConvention.cs b/src/Microsoft.AspNetCore.OData/Routing/Conventions/EntitySetRoutingConvention.cs
index 53c7563a4..158430c94 100644
--- a/src/Microsoft.AspNetCore.OData/Routing/Conventions/EntitySetRoutingConvention.cs
+++ b/src/Microsoft.AspNetCore.OData/Routing/Conventions/EntitySetRoutingConvention.cs
@@ -97,7 +97,7 @@ public virtual bool AppliesToAction(ODataControllerActionContext context)
return ProcessEntitySetAction(actionPrefix, entitySet, castType, context, action);
}
- private static bool ProcessEntitySetAction(string actionName, IEdmEntitySet entitySet, IEdmStructuredType castType,
+ private bool ProcessEntitySetAction(string actionName, IEdmEntitySet entitySet, IEdmStructuredType castType,
ODataControllerActionContext context, ActionModel action)
{
StringComparison actionNameComparison = context.Options?.RouteOptions?.EnableActionNameCaseInsensitive == true ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
@@ -126,21 +126,25 @@ private static bool ProcessEntitySetAction(string actionName, IEdmEntitySet enti
ODataPathTemplate template = new ODataPathTemplate(segments);
action.AddSelector("Get", context.Prefix, context.Model, template, context.Options?.RouteOptions);
- // GET ~/Customers/$count or GET ~/Customers/Ns.VipCustomer/$count
- segments = new List
+ if (CanApplyDollarCount(entitySet, context.Options?.RouteOptions))
{
- new EntitySetSegmentTemplate(entitySet)
- };
+ // GET ~/Customers/$count or GET ~/Customers/Ns.VipCustomer/$count
+ segments = new List
+ {
+ new EntitySetSegmentTemplate(entitySet)
+ };
- if (castType != null)
- {
- segments.Add(new CastSegmentTemplate(castCollectionType, entityCollectionType, entitySet));
- }
+ if (castType != null)
+ {
+ segments.Add(new CastSegmentTemplate(castCollectionType, entityCollectionType, entitySet));
+ }
- segments.Add(CountSegmentTemplate.Instance);
+ segments.Add(CountSegmentTemplate.Instance);
+
+ template = new ODataPathTemplate(segments);
+ action.AddSelector("Get", context.Prefix, context.Model, template, context.Options?.RouteOptions);
+ }
- template = new ODataPathTemplate(segments);
- action.AddSelector("Get", context.Prefix, context.Model, template, context.Options?.RouteOptions);
return true;
}
else if (actionName.Equals("Post", actionNameComparison) || actionName.Equals($"Post{entitySet.EntityType().Name}", actionNameComparison))
@@ -183,5 +187,14 @@ private static bool ProcessEntitySetAction(string actionName, IEdmEntitySet enti
return false;
}
+
+ ///
+ /// Tests whether to apply $count on the .
+ ///
+ /// The entity set to test.
+ /// The route options.
+ /// True/false to identify whether to apply $count.
+ protected virtual bool CanApplyDollarCount(IEdmEntitySet entitySet, ODataRouteOptions routeOptions)
+ => routeOptions != null ? routeOptions.EnableDollarCountRouting : false;
}
}
diff --git a/src/Microsoft.AspNetCore.OData/Routing/Conventions/NavigationRoutingConvention.cs b/src/Microsoft.AspNetCore.OData/Routing/Conventions/NavigationRoutingConvention.cs
index ca21605a2..42a9d9b7a 100644
--- a/src/Microsoft.AspNetCore.OData/Routing/Conventions/NavigationRoutingConvention.cs
+++ b/src/Microsoft.AspNetCore.OData/Routing/Conventions/NavigationRoutingConvention.cs
@@ -141,7 +141,7 @@ public virtual bool AppliesToAction(ODataControllerActionContext context)
AddSelector(method, context, action, navigationSource, declared, declaringEntityType, navigationProperty, hasKeyParameter, false);
- if (CanApplyDollarCount(navigationProperty, method))
+ if (CanApplyDollarCount(navigationProperty, method, context.Options?.RouteOptions))
{
AddSelector(method, context, action, navigationSource, declared, declaringEntityType, navigationProperty, hasKeyParameter, true);
}
@@ -244,11 +244,25 @@ internal static string SplitActionName(string actionName, out string property, o
return method;
}
- // OData spec: To request only the number of items of a collection of entities or items of a collection-valued property,
- // the client issues a GET request with /$count appended to the resource path of the collection.
- private static bool CanApplyDollarCount(IEdmNavigationProperty edmProperty, string method)
+ ///
+ /// OData spec: To request only the number of items of a collection of entities or items of a collection-valued property,
+ /// the client issues a GET request with /$count appended to the resource path of the collection.
+ ///
+ /// The property to test.
+ /// The http method.
+ /// The route options.
+ /// True/false to identify whether to apply $count.
+ protected virtual bool CanApplyDollarCount(IEdmNavigationProperty edmProperty, string method, ODataRouteOptions routeOptions)
{
- Contract.Assert(edmProperty != null);
+ if(edmProperty == null)
+ {
+ throw Error.ArgumentNull(nameof(edmProperty));
+ }
+
+ if (routeOptions != null && !routeOptions.EnableDollarCountRouting)
+ {
+ return false;
+ }
return method == "Get" && edmProperty.Type.IsCollection();
}
diff --git a/src/Microsoft.AspNetCore.OData/Routing/Conventions/PropertyRoutingConvention.cs b/src/Microsoft.AspNetCore.OData/Routing/Conventions/PropertyRoutingConvention.cs
index bc6134263..cc1824fcc 100644
--- a/src/Microsoft.AspNetCore.OData/Routing/Conventions/PropertyRoutingConvention.cs
+++ b/src/Microsoft.AspNetCore.OData/Routing/Conventions/PropertyRoutingConvention.cs
@@ -113,7 +113,7 @@ public virtual bool AppliesToAction(ODataControllerActionContext context)
return false;
}
- if (!CanApply(edmProperty, method))
+ if (!CanApply(edmProperty, method, context.Options?.RouteOptions))
{
return false;
}
@@ -166,12 +166,12 @@ public virtual bool AppliesToAction(ODataControllerActionContext context)
AddSelector(method, context, action, navigationSource, (IEdmStructuralProperty)edmProperty, castComplexType, declaringEntityType, false, false);
- if (CanApplyDollarCount(edmProperty, method))
+ if (CanApplyDollarCount(edmProperty, method, context.Options?.RouteOptions))
{
AddSelector(method, context, action, navigationSource, (IEdmStructuralProperty)edmProperty, castComplexType, declaringEntityType, false, true);
}
- if (CanApplyDollarValue(edmProperty, method))
+ if (CanApplyDollarValue(edmProperty, method, context.Options?.RouteOptions))
{
AddSelector(method, context, action, navigationSource, (IEdmStructuralProperty)edmProperty, castComplexType, declaringEntityType, true, false);
}
@@ -282,9 +282,19 @@ private static string Match(string text, out string declared)
return text;
}
- private static bool CanApply(IEdmProperty edmProperty, string method)
+ ///
+ /// Tests whether to apply routings for the given property.
+ ///
+ /// The property to test.
+ /// The http method.
+ /// The route options.
+ /// True/false to identify whether to apply routings for the given property.
+ protected virtual bool CanApply(IEdmProperty edmProperty, string method, ODataRouteOptions routeOptions)
{
- Contract.Assert(edmProperty != null);
+ if (edmProperty == null)
+ {
+ throw Error.ArgumentNull(nameof(edmProperty));
+ }
bool isCollection = edmProperty.Type.IsCollection();
@@ -311,20 +321,48 @@ private static bool CanApply(IEdmProperty edmProperty, string method)
return true;
}
- // OData spec: To retrieve the raw value of a primitive type property, the client sends a GET request to the property value URL.
- // So, let's apply $value for the "Get" and non-collection primitive property
- private static bool CanApplyDollarValue(IEdmProperty edmProperty, string method)
+ ///
+ /// OData spec: To retrieve the raw value of a primitive type property, the client sends a GET request to the property value URL.
+ /// So, let's apply $value for the "Get" and non-collection primitive property
+ ///
+ /// The property to test.
+ /// The http method.
+ /// The route options.
+ /// True/false to identify whether to apply $value.
+ protected virtual bool CanApplyDollarValue(IEdmProperty edmProperty, string method, ODataRouteOptions routeOptions)
{
- Contract.Assert(edmProperty != null);
+ if (edmProperty == null)
+ {
+ throw Error.ArgumentNull(nameof(edmProperty));
+ }
+
+ if (routeOptions != null && !routeOptions.EnableDollarValueRouting)
+ {
+ return false;
+ }
return method == "Get" && !edmProperty.Type.IsCollection() && (edmProperty.Type.IsPrimitive() || edmProperty.Type.IsEnum());
}
- // OData spec: To request only the number of items of a collection of entities or items of a collection-valued property,
- // the client issues a GET request with /$count appended to the resource path of the collection.
- private static bool CanApplyDollarCount(IEdmProperty edmProperty, string method)
+ ///
+ /// OData spec: To request only the number of items of a collection of entities or items of a collection-valued property,
+ /// the client issues a GET request with /$count appended to the resource path of the collection.
+ ///
+ /// The property to test.
+ /// The http method.
+ /// The route options.
+ /// True/false to identify whether to apply $count.
+ protected virtual bool CanApplyDollarCount(IEdmProperty edmProperty, string method, ODataRouteOptions routeOptions)
{
- Contract.Assert(edmProperty != null);
+ if (edmProperty == null)
+ {
+ throw Error.ArgumentNull(nameof(edmProperty));
+ }
+
+ if (routeOptions != null && !routeOptions.EnableDollarCountRouting)
+ {
+ return false;
+ }
return method == "Get" && edmProperty.Type.IsCollection();
}
diff --git a/src/Microsoft.AspNetCore.OData/Routing/ODataRouteOptions.cs b/src/Microsoft.AspNetCore.OData/Routing/ODataRouteOptions.cs
index eeaad4308..28c2e1b32 100644
--- a/src/Microsoft.AspNetCore.OData/Routing/ODataRouteOptions.cs
+++ b/src/Microsoft.AspNetCore.OData/Routing/ODataRouteOptions.cs
@@ -33,6 +33,16 @@ public ODataRouteOptions()
_enableUnqualifiedOperationCall = true;
}
+ ///
+ /// Gets/sets a value indicating whether to enable $count in conventional routing.
+ ///
+ public bool EnableDollarCountRouting { get; set; } = true;
+
+ ///
+ /// Gets/sets a value indicating whether to enable $value in conventional routing.
+ ///
+ public bool EnableDollarValueRouting { get; set; } = true;
+
///
/// Gets/sets a value indicating whether to enable case insensitive for the action name in conventional routing.
///
diff --git a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net6.bsl b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net6.bsl
index 44364d55d..6eb2500c8 100644
--- a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net6.bsl
+++ b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net6.bsl
@@ -1860,6 +1860,8 @@ public class Microsoft.AspNetCore.OData.Routing.ODataRouteOptions {
bool EnableActionNameCaseInsensitive { public get; public set; }
bool EnableControllerNameCaseInsensitive { public get; public set; }
+ bool EnableDollarCountRouting { public get; public set; }
+ bool EnableDollarValueRouting { public get; public set; }
bool EnableKeyAsSegment { public get; public set; }
bool EnableKeyInParenthesis { public get; public set; }
bool EnableNonParenthesisForEmptyParameterFunction { public get; public set; }
@@ -3159,6 +3161,7 @@ public class Microsoft.AspNetCore.OData.Routing.Conventions.EntitySetRoutingConv
public virtual bool AppliesToAction (Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context)
public virtual bool AppliesToController (Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context)
+ protected virtual bool CanApplyDollarCount (Microsoft.OData.Edm.IEdmEntitySet entitySet, Microsoft.AspNetCore.OData.Routing.ODataRouteOptions routeOptions)
}
public class Microsoft.AspNetCore.OData.Routing.Conventions.FunctionRoutingConvention : Microsoft.AspNetCore.OData.Routing.Conventions.OperationRoutingConvention, IODataControllerActionConvention {
@@ -3186,6 +3189,7 @@ public class Microsoft.AspNetCore.OData.Routing.Conventions.NavigationRoutingCon
public virtual bool AppliesToAction (Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context)
public virtual bool AppliesToController (Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context)
+ protected virtual bool CanApplyDollarCount (Microsoft.OData.Edm.IEdmNavigationProperty edmProperty, string method, Microsoft.AspNetCore.OData.Routing.ODataRouteOptions routeOptions)
}
public class Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext {
@@ -3218,6 +3222,9 @@ public class Microsoft.AspNetCore.OData.Routing.Conventions.PropertyRoutingConve
public virtual bool AppliesToAction (Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context)
public virtual bool AppliesToController (Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context)
+ protected virtual bool CanApply (Microsoft.OData.Edm.IEdmProperty edmProperty, string method, Microsoft.AspNetCore.OData.Routing.ODataRouteOptions routeOptions)
+ protected virtual bool CanApplyDollarCount (Microsoft.OData.Edm.IEdmProperty edmProperty, string method, Microsoft.AspNetCore.OData.Routing.ODataRouteOptions routeOptions)
+ protected virtual bool CanApplyDollarValue (Microsoft.OData.Edm.IEdmProperty edmProperty, string method, Microsoft.AspNetCore.OData.Routing.ODataRouteOptions routeOptions)
}
public class Microsoft.AspNetCore.OData.Routing.Conventions.RefRoutingConvention : IODataControllerActionConvention {
diff --git a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl
index 44364d55d..6eb2500c8 100644
--- a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl
+++ b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl
@@ -1860,6 +1860,8 @@ public class Microsoft.AspNetCore.OData.Routing.ODataRouteOptions {
bool EnableActionNameCaseInsensitive { public get; public set; }
bool EnableControllerNameCaseInsensitive { public get; public set; }
+ bool EnableDollarCountRouting { public get; public set; }
+ bool EnableDollarValueRouting { public get; public set; }
bool EnableKeyAsSegment { public get; public set; }
bool EnableKeyInParenthesis { public get; public set; }
bool EnableNonParenthesisForEmptyParameterFunction { public get; public set; }
@@ -3159,6 +3161,7 @@ public class Microsoft.AspNetCore.OData.Routing.Conventions.EntitySetRoutingConv
public virtual bool AppliesToAction (Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context)
public virtual bool AppliesToController (Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context)
+ protected virtual bool CanApplyDollarCount (Microsoft.OData.Edm.IEdmEntitySet entitySet, Microsoft.AspNetCore.OData.Routing.ODataRouteOptions routeOptions)
}
public class Microsoft.AspNetCore.OData.Routing.Conventions.FunctionRoutingConvention : Microsoft.AspNetCore.OData.Routing.Conventions.OperationRoutingConvention, IODataControllerActionConvention {
@@ -3186,6 +3189,7 @@ public class Microsoft.AspNetCore.OData.Routing.Conventions.NavigationRoutingCon
public virtual bool AppliesToAction (Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context)
public virtual bool AppliesToController (Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context)
+ protected virtual bool CanApplyDollarCount (Microsoft.OData.Edm.IEdmNavigationProperty edmProperty, string method, Microsoft.AspNetCore.OData.Routing.ODataRouteOptions routeOptions)
}
public class Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext {
@@ -3218,6 +3222,9 @@ public class Microsoft.AspNetCore.OData.Routing.Conventions.PropertyRoutingConve
public virtual bool AppliesToAction (Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context)
public virtual bool AppliesToController (Microsoft.AspNetCore.OData.Routing.Conventions.ODataControllerActionContext context)
+ protected virtual bool CanApply (Microsoft.OData.Edm.IEdmProperty edmProperty, string method, Microsoft.AspNetCore.OData.Routing.ODataRouteOptions routeOptions)
+ protected virtual bool CanApplyDollarCount (Microsoft.OData.Edm.IEdmProperty edmProperty, string method, Microsoft.AspNetCore.OData.Routing.ODataRouteOptions routeOptions)
+ protected virtual bool CanApplyDollarValue (Microsoft.OData.Edm.IEdmProperty edmProperty, string method, Microsoft.AspNetCore.OData.Routing.ODataRouteOptions routeOptions)
}
public class Microsoft.AspNetCore.OData.Routing.Conventions.RefRoutingConvention : IODataControllerActionConvention {
diff --git a/test/Microsoft.AspNetCore.OData.Tests/Routing/Conventions/EntitySetRoutingConventionTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Routing/Conventions/EntitySetRoutingConventionTests.cs
index 0a4d474e7..4f5ea539f 100644
--- a/test/Microsoft.AspNetCore.OData.Tests/Routing/Conventions/EntitySetRoutingConventionTests.cs
+++ b/test/Microsoft.AspNetCore.OData.Tests/Routing/Conventions/EntitySetRoutingConventionTests.cs
@@ -80,6 +80,39 @@ public void AppliesToActionForGetActionWorksAsExpected(Type controllerType, stri
action.Selectors.Select(s => s.AttributeRouteModel.Template));
}
+ [Theory]
+ [InlineData(typeof(CustomersController), "Get", 1, false)]
+ [InlineData(typeof(CustomersController), "Get", 2, true)]
+ [InlineData(typeof(CustomersController), "GetCustomersFromVipCustomer", 1, false)]
+ [InlineData(typeof(CustomersController), "GetCustomersFromVipCustomer", 2, true)]
+ public void AppliesToActionForGetActionAddDollarCountAsExpectedBasedOnConfiguration(Type controllerType, string actionName, int expectCount, bool enableDollarCount)
+ {
+ // Arrange
+ ControllerModel controller = ControllerModelHelpers.BuildControllerModel(controllerType, actionName);
+ ActionModel action = controller.Actions.First();
+
+ ODataControllerActionContext context = ODataControllerActionContextHelpers.BuildContext(string.Empty, EdmModel, controller);
+ context.Action = action;
+ context.Options.RouteOptions.EnableDollarCountRouting = enableDollarCount;
+
+ EntitySetRoutingConvention entitySetConvention = ConventionHelpers.CreateConvention();
+
+ // Act
+ bool returnValue = entitySetConvention.AppliesToAction(context);
+ Assert.True(returnValue);
+
+ // Assert
+ Assert.Equal(expectCount, action.Selectors.Count);
+ if (enableDollarCount)
+ {
+ Assert.Contains("/$count", string.Join(",", action.Selectors.Select(s => s.AttributeRouteModel.Template)));
+ }
+ else
+ {
+ Assert.DoesNotContain("/$count", string.Join(",", action.Selectors.Select(s => s.AttributeRouteModel.Template)));
+ }
+ }
+
[Theory]
[InlineData(typeof(CustomersController), "Post", "/Customers", false)]
[InlineData(typeof(CustomersController), "PostFromVipCustomer", "/Customers/NS.VipCustomer", false)]
diff --git a/test/Microsoft.AspNetCore.OData.Tests/Routing/Conventions/NavigationRoutingConventionTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Routing/Conventions/NavigationRoutingConventionTests.cs
index 504e656c0..1976a789b 100644
--- a/test/Microsoft.AspNetCore.OData.Tests/Routing/Conventions/NavigationRoutingConventionTests.cs
+++ b/test/Microsoft.AspNetCore.OData.Tests/Routing/Conventions/NavigationRoutingConventionTests.cs
@@ -144,6 +144,37 @@ public void NavigationRoutingConventionTestDataRunsAsExpected(Type controllerTyp
Assert.Equal(templates, action.Selectors.Select(s => s.AttributeRouteModel.Template));
}
+ [Theory]
+ [InlineData(typeof(CustomersController), "GetOrders", 2, false)]
+ [InlineData(typeof(CustomersController), "GetOrders", 4, true)]
+ [InlineData(typeof(CustomersController), "GetSubOrdersFromVipCustomer", 2, false)]
+ [InlineData(typeof(CustomersController), "GetSubOrdersFromVipCustomer", 4, true)]
+ public void NavigationRoutingConventionAddDollarCountAsExpectedBasedOnConfiguration(Type controllerType, string actionName, int expectCount, bool enableDollarCount)
+ {
+ // Arrange
+ ControllerModel controller = ControllerModelHelpers.BuildControllerModel(controllerType, actionName);
+ ActionModel action = controller.Actions.First();
+
+ ODataControllerActionContext context = ODataControllerActionContextHelpers.BuildContext(string.Empty, EdmModel, controller);
+ context.Action = action;
+ context.Options.RouteOptions.EnableDollarCountRouting = enableDollarCount;
+
+ // Act
+ bool returnValue = NavigationConvention.AppliesToAction(context);
+ Assert.True(returnValue);
+
+ // Assert
+ Assert.Equal(expectCount, action.Selectors.Count);
+ if (enableDollarCount)
+ {
+ Assert.Contains("/$count", string.Join(",", action.Selectors.Select(s => s.AttributeRouteModel.Template)));
+ }
+ else
+ {
+ Assert.DoesNotContain("/$count", string.Join(",", action.Selectors.Select(s => s.AttributeRouteModel.Template)));
+ }
+ }
+
[Theory]
[InlineData("PostToName")]
[InlineData("Get")]
diff --git a/test/Microsoft.AspNetCore.OData.Tests/Routing/Conventions/PropertyRoutingConventionTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Routing/Conventions/PropertyRoutingConventionTests.cs
index 872022ff0..cd21b3381 100644
--- a/test/Microsoft.AspNetCore.OData.Tests/Routing/Conventions/PropertyRoutingConventionTests.cs
+++ b/test/Microsoft.AspNetCore.OData.Tests/Routing/Conventions/PropertyRoutingConventionTests.cs
@@ -188,6 +188,37 @@ public void PropertyRoutingConventionTestDataRunsAsExpected(Type controllerType,
Assert.Equal(templates, action.Selectors.Select(s => s.AttributeRouteModel.Template));
}
+ [Theory]
+ [InlineData(typeof(MeController), "GetEmails", 1, false)]
+ [InlineData(typeof(MeController), "GetEmails", 2, true)]
+ [InlineData(typeof(MeController), "GetSubLocationsOfUsAddressFromVipCustomer", 1, false)]
+ [InlineData(typeof(MeController), "GetSubLocationsOfUsAddressFromVipCustomer", 2, true)]
+ public void PropertyRoutingConventionAddDollarCountAsExpectedBasedOnConfiguration(Type controllerType, string actionName, int expectCount, bool enableDollarCount)
+ {
+ // Arrange
+ ControllerModel controller = ControllerModelHelpers.BuildControllerModel(controllerType, actionName);
+ ActionModel action = controller.Actions.First();
+
+ ODataControllerActionContext context = ODataControllerActionContextHelpers.BuildContext(string.Empty, EdmModel, controller);
+ context.Action = action;
+ context.Options.RouteOptions.EnableDollarCountRouting = enableDollarCount;
+
+ // Act
+ bool returnValue = PropertyConvention.AppliesToAction(context);
+ Assert.True(returnValue);
+
+ // Assert
+ Assert.Equal(expectCount, action.Selectors.Count);
+ if (enableDollarCount)
+ {
+ Assert.Contains("/$count", string.Join(",", action.Selectors.Select(s => s.AttributeRouteModel.Template)));
+ }
+ else
+ {
+ Assert.DoesNotContain("/$count", string.Join(",", action.Selectors.Select(s => s.AttributeRouteModel.Template)));
+ }
+ }
+
[Theory]
[InlineData("PostToName")]
[InlineData("Get")]