OData / OData/AspNetCoreOData

$select on ICollection with EFCore CosmosDB provider throws ICollection to IQueryable casting exception

Open
#622 3 comments 1 reaction 1 assignee View on GitHub

@habbes is already working on this.

Since Jun 21, 2022.

bug
Dominant language
C#
Stars
505
Forks
186
PR merge metrics
No merged PRs in 30d

Description

Assemblies affected
ASP.NET Core OData 8.0.10 with Microsoft.EntityFrameworkCore.Cosmos 6.0.4

Describe the bug
$select on ICollection property fails with exception

Expression of type 'System.Collections.Generic.ICollection`1[SampleIksApi.Models.PartnerTenantSubscription]' cannot be used for parameter of type 'System.Linq.IQueryable`1[SampleIksApi.Models.PartnerTenantSubscription]' of method 'System.Linq.IQueryable`1[SampleIksApi.Models.PartnerTenantSubscription] Take[PartnerTenantSubscription](System.Linq.IQueryable`1[SampleIksApi.Models.PartnerTenantSubscription], Int32)' (Parameter 'arg0')

I have described the issue in greater detail at my stackoverflow question here - https://stackoverflow.com/questions/72570499/querying-for-owned-collection-in-cosmosdb-throws-exception-when-using-odata-sel

Reproduce steps
The simplest set of steps to reproduce the issue. If possible, reference a commit that demonstrates the issue.

  1. Clone repo at https://github.com/heyhari7/SampleIksApi/tree/master
  2. Update secrets config to point to CosmosDB
  3. Make a OData call to api/odata/v1.0.0/PartnerTenantSubscriptions?$select=PartnerMPNId,ReportingDate,CustomerTenantId,PartnerAssociationType,CustomerTenantName,Subscriptions

Data Model
Please share your Data model, for example, your C# class.

public class PartnerTenantSubscriptions
    {
        public string PartnerMPNId { get; set; }
        public string PartnerName { get; set; }
        public string CustomerTenantId { get; set; }
        public string CustomerTenantName { get; set; }
        public string PartnerAssociationType { get; set; }
        public DateTimeOffset ReportingDate { get; set; }
        public string id { get; set; }
        public ICollection<PartnerTenantSubscription> Subscriptions { get; set; }
    }

    public class PartnerTenantSubscription
    {
        public string SubscriptionId { get; set; }
        public string SubscriptionState { get; set; }
        public DateTimeOffset? PaidStartDate { get; set; }
        public DateTimeOffset? PaidEndDate { get; set; }
        public DateTimeOffset? TrialEndDate { get; set; }
        public bool? IsAutoRenew { get; set; }
        public DateTimeOffset? StartDate { get; set; }
        public DateTimeOffset? EndDate { get; set; }
    }

EDM (CSDL) Model

<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
    <edmx:DataServices>
        <Schema Namespace="SampleIksApi.Models" xmlns="http://docs.oasis-open.org/odata/ns/edm">
            <EntityType Name="PartnerTenantSubscriptions">
                <Key>
                    <PropertyRef Name="id" />
                </Key>
                <Property Name="PartnerMPNId" Type="Edm.String" Nullable="false" />
                <Property Name="PartnerName" Type="Edm.String" Nullable="false" />
                <Property Name="CustomerTenantId" Type="Edm.String" Nullable="false" />
                <Property Name="CustomerTenantName" Type="Edm.String" Nullable="false" />
                <Property Name="PartnerAssociationType" Type="Edm.String" Nullable="false" />
                <Property Name="ReportingDate" Type="Edm.DateTimeOffset" Nullable="false" />
                <Property Name="id" Type="Edm.String" Nullable="false" />
                <Property Name="Subscriptions" Type="Collection(SampleIksApi.Models.PartnerTenantSubscription)" />
            </EntityType>
            <ComplexType Name="PartnerTenantSubscription">
                <Property Name="SubscriptionId" Type="Edm.String" Nullable="false" />
                <Property Name="SubscriptionState" Type="Edm.String" Nullable="false" />
                <Property Name="PaidStartDate" Type="Edm.DateTimeOffset" />
                <Property Name="PaidEndDate" Type="Edm.DateTimeOffset" />
                <Property Name="TrialEndDate" Type="Edm.DateTimeOffset" />
                <Property Name="IsAutoRenew" Type="Edm.Boolean" />
                <Property Name="StartDate" Type="Edm.DateTimeOffset" />
                <Property Name="EndDate" Type="Edm.DateTimeOffset" />
            </ComplexType>
        </Schema>
        <Schema Namespace="Default" xmlns="http://docs.oasis-open.org/odata/ns/edm">
            <EntityContainer Name="Container">
                <EntitySet Name="PartnerTenantSubscriptions" EntityType="SampleIksApi.Models.PartnerTenantSubscriptions" />
            </EntityContainer>
        </Schema>
    </edmx:DataServices>
</edmx:Edmx>

Request/Response
Uri - https://localhost:7157/api/odata/v1.0.0/PartnerTenantSubscriptions?$select=PartnerMPNId,ReportingDate,CustomerTenantId,PartnerAssociationType,CustomerTenantName,Subscriptions
Response -
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.ArgumentException: Expression of type 'System.Collections.Generic.ICollection1[SampleIksApi.Models.PartnerTenantSubscription]' cannot be used for parameter of type 'System.Linq.IQueryable1[SampleIksApi.Models.PartnerTenantSubscription]' of method 'System.Linq.IQueryable1[SampleIksApi.Models.PartnerTenantSubscription] Take[PartnerTenantSubscription](System.Linq.IQueryable1[SampleIksApi.Models.PartnerTenantSubscription], Int32)' (Parameter 'arg0')
at System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arguments, ParameterInfo pi, String methodParamName, String argumentParamName, Int32 index)
at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, Expression arg0, Expression arg1)
at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, IEnumerable1 arguments) at System.Linq.Expressions.MethodCallExpression.Update(Expression object, IEnumerable1 arguments)
at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.Visit(Expression expression)
at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.Visit(Expression expression)
at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.VisitMemberAssignment(MemberAssignment memberAssignment)
at System.Linq.Expressions.ExpressionVisitor.VisitMemberBinding(MemberBinding node)
at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.VisitMemberInit(MemberInitExpression memberInitExpression)
at System.Linq.Expressions.MemberInitExpression.Accept(ExpressionVisitor visitor)
at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.Visit(Expression expression)
at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.VisitMemberAssignment(MemberAssignment memberAssignment)
at System.Linq.Expressions.ExpressionVisitor.VisitMemberBinding(MemberBinding node)
at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.VisitMemberInit(MemberInitExpression memberInitExpression)
at System.Linq.Expressions.MemberInitExpression.Accept(ExpressionVisitor visitor)
at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.Visit(Expression expression)
at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.VisitMemberAssignment(MemberAssignment memberAssignment)
at System.Linq.Expressions.ExpressionVisitor.VisitMemberBinding(MemberBinding node)
at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.VisitMemberInit(MemberInitExpression memberInitExpression)
at System.Linq.Expressions.MemberInitExpression.Accept(ExpressionVisitor visitor)
at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.Visit(Expression expression)
at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.Translate(SelectExpression selectExpression, Expression expression)
at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosQueryableMethodTranslatingExpressionVisitor.TranslateSelect(ShapedQueryExpression source, LambdaExpression selector)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosQueryableMethodTranslatingExpressionVisitor.Visit(Expression expression)
at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass9_01.<Execute>b__0() at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func1 compiler)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable1.GetEnumerator() at System.Collections.Generic.List1..ctor(IEnumerable1 collection) at Microsoft.AspNetCore.OData.Query.Container.TruncatedCollection1..ctor(IQueryable1 source, Int32 pageSize, Boolean parameterize) at Microsoft.AspNetCore.OData.Query.ODataQueryOptions.LimitResults[T](IQueryable1 queryable, Int32 limit, Boolean parameterize, Boolean& resultsLimited)
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle.InvokeMethod(Object target, Span`1& arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Microsoft.AspNetCore.OData.Query.ODataQueryOptions.LimitResults(IQueryable queryable, Int32 limit, Boolean parameterize, Boolean& resultsLimited)
at Microsoft.AspNetCore.OData.Query.ODataQueryOptions.ApplyPaging(IQueryable result, ODataQuerySettings querySettings)
at Microsoft.AspNetCore.OData.Query.ODataQueryOptions.ApplyTo(IQueryable query, ODataQuerySettings querySettings)
at Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.ExecuteQuery(Object responseValue, IQueryable singleResultCollection, ControllerActionDescriptor actionDescriptor, HttpRequest request)
at Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.OnActionExecuted(ActionExecutedContext actionExecutedContext, Object responseValue, IQueryable singleResultCollection, ControllerActionDescriptor actionDescriptor, HttpRequest request)
at Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.OnActionExecuted(ActionExecutedContext actionExecutedContext)
at Microsoft.AspNetCore.Mvc.Filters.ActionFilterAttribute.OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at SampleIksApi.Common.Startup.<>c.<b__5_0>d.MoveNext() in C:\Users\haridura\source\repos\SampleIksApi\SampleIksApi\Startup.cs:line 37
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

HEADERS

Accept: /
Connection: keep-alive
Host: localhost:7157
User-Agent: PostmanRuntime/7.29.0
Accept-Encoding: gzip, deflate, br
Cache-Control: no-cache
Prefer: odata.maxpagesize=50
Postman-Token: 4546bf67-565e-40f7-9176-767b7fbc5b95

Expected behavior
Response with only the properties in the select clause included

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.