OData / WebApi

OData Web API: A server library built upon ODataLib and WebApi
https://docs.microsoft.com/odata
Other
853 stars 476 forks source link

Lambda Error: No coercion operator is defined between types #1461

Open NetTecture opened 6 years ago

NetTecture commented 6 years ago

System.InvalidOperationException HResult=0x80131509 Message=No coercion operator is defined between types 'Microsoft.AspNet.OData.Query.Expressions.SelectExpandBinder+SelectAllAndExpand1[Api.Odata.OpsTenant]' and 'Api.Odata.OpsTenant'. Source=System.Linq.Expressions StackTrace: at System.Linq.Expressions.Expression.GetUserDefinedCoercionOrThrow(ExpressionType coercionType, Expression expression, Type convertToType) at System.Linq.Expressions.Expression.Convert(Expression expression, Type type, MethodInfo method) at System.Linq.Expressions.Expression.Convert(Expression expression, Type type) at Remotion.Linq.Clauses.ResultOperators.CastResultOperator.GetOutputDataInfo(IStreamedDataInfo inputInfo) at Remotion.Linq.QueryModel.<>c.<GetOutputDataInfo>b__10_0(IStreamedDataInfo current, ResultOperatorBase resultOperator) at System.Linq.Enumerable.Aggregate[TSource,TAccumulate](IEnumerable1 source, TAccumulate seed, Func3 func) at Remotion.Linq.QueryModel.GetOutputDataInfo() at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](Expression query, IQueryModelGenerator queryModelGenerator, IDatabase database, IDiagnosticsLogger1 logger, Type contextType) at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass13_01.<Execute>b__0() at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func1 compiler) 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.EntityQueryProvider.Execute[TResult](Expression expression) at Remotion.Linq.QueryableBase1.GetEnumerator() at System.Collections.Generic.LargeArrayBuilder1.AddRange(IEnumerable1 items) at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable1 source) at System.Linq.Enumerable.ToArray[TSource](IEnumerable1 source) at Api.Odata.Web.Controllers.OpsTenantController.Get(ODataQueryOptions`1 options) in C:\Work\Source.Backend\Api.Odata.Web\Controllers\OpsTenantController.cs:line 49

The underlying code is:

        var qry1 = Repository.OpsTenant
            .Take(10)
            .Include(x => x.Buildings)
            .ProjectTo<Api.Odata.OpsTenant>(x => x.Buildings);

        var qry1a = options.ApplyTo(qry1).Cast<Api.Odata.OpsTenant>();

and leads to the following lambda (note: ProjectTo is totally removed):

.Call System.Linq.Queryable.Cast(.Call System.Linq.Queryable.Select( .Call System.Linq.Queryable.Take( .Call System.Linq.Queryable.OrderBy( .Call System.Linq.Queryable.Select( .Call Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.Include( .Call System.Linq.Queryable.Take( .Constant<Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable1[Data.Core.OpsTenant]>(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable1[Data.Core.OpsTenant]), 10), '(.Lambda #Lambda1<System.Func2[Data.Core.OpsTenant,System.Collections.Generic.ICollection1[Data.Core.CoreBuilding]]>)) , '(.Lambda #Lambda2<System.Func2[Data.Core.OpsTenant,Api.Odata.OpsTenant]>)), '(.Lambda #Lambda3<System.Func2[Api.Odata.OpsTenant,System.Guid]>)), .Constant<Microsoft.AspNet.OData.Query.Expressions.LinqParameterContainer+TypedLinqParameterContainer1[System.Int32]>(Microsoft.AspNet.OData.Query.Expressions.LinqParameterContainer+TypedLinqParameterContainer1[System.Int32]).TypedProperty) , '(.Lambda #Lambda4<System.Func2[Api.Odata.OpsTenant,Microsoft.AspNet.OData.Query.Expressions.SelectExpandBinder+SelectAllAndExpand1[Api.Odata.OpsTenant]]>)) )

.Lambda #Lambda1<System.Func2[Data.Core.OpsTenant,System.Collections.Generic.ICollection1[Data.Core.CoreBuilding]]>(Data.Core.OpsTenant $x) { $x.Buildings }

.Lambda #Lambda2<System.Func2[Data.Core.OpsTenant,Api.Odata.OpsTenant]>(Data.Core.OpsTenant $dtoOpsTenant) { .New Api.Odata.OpsTenant(){ Identity = $dtoOpsTenant.Identity, Name = $dtoOpsTenant.Name, Buildings = .Call System.Linq.Enumerable.ToList(.Call System.Linq.Enumerable.Select( $dtoOpsTenant.Buildings, .Lambda #Lambda5<System.Func2[Data.Core.CoreBuilding,Api.Odata.CoreBuilding]>)) } }

.Lambda #Lambda3<System.Func`2[Api.Odata.OpsTenant,System.Guid]>(Api.Odata.OpsTenant $$it) { $$it.Identity }

.Lambda #Lambda4<System.Func2[Api.Odata.OpsTenant,Microsoft.AspNet.OData.Query.Expressions.SelectExpandBinder+SelectAllAndExpand1[Api.Odata.OpsTenant]]>(Api.Odata.OpsTenant $var1) { .New Microsoft.AspNet.OData.Query.Expressions.SelectExpandBinder+SelectAllAndExpand1[Api.Odata.OpsTenant](){ ModelID = "df7f2288-4d2c-44bc-9497-19bf3e69b983", Instance = $var1, UseInstanceForProperties = True, Container = .New Microsoft.AspNet.OData.Query.Expressions.PropertyContainer+NamedProperty1[System.Collections.Generic.IEnumerable1[Microsoft.AspNet.OData.Query.Expressions.SelectExpandBinder+SelectAll1[Api.Odata.CoreBuilding]]]() { Name = "Buildings", Value = .Call System.Linq.Enumerable.Select( $var1.Buildings, .Lambda #Lambda6<System.Func2[Api.Odata.CoreBuilding,Microsoft.AspNet.OData.Query.Expressions.SelectExpandBinder+SelectAll1[Api.Odata.CoreBuilding]]>) } } }

.Lambda #Lambda5<System.Func`2[Data.Core.CoreBuilding,Api.Odata.CoreBuilding]>(Data.Core.CoreBuilding $dtoCoreBuilding) { .New Api.Odata.CoreBuilding(){ Identity = $dtoCoreBuilding.Identity, Name = $dtoCoreBuilding.LongName } }

.Lambda #Lambda6<System.Func2[Api.Odata.CoreBuilding,Microsoft.AspNet.OData.Query.Expressions.SelectExpandBinder+SelectAll1[Api.Odata.CoreBuilding]]>(Api.Odata.CoreBuilding $var2) { .New Microsoft.AspNet.OData.Query.Expressions.SelectExpandBinder+SelectAll`1[Api.Odata.CoreBuilding](){ ModelID = "df7f2288-4d2c-44bc-9497-19bf3e69b983", Instance = $var2, UseInstanceForProperties = True } }

Looksl ike both, a N+1 problem (doo many hardcoded subqueries) as well as definitely an error in the generated lambda.

NetTecture commented 5 years ago

And 5 months later: Assigned, not worked on.

Just as update - it is STILL there and it still blocks using Odata in custom Lambda.

TheAifam5 commented 5 years ago

It's been already much more than 5 months. I'm getting similar error with a following code:

var results = this.repository.GetAll().Where(u => u.IsBanned);
return options.ApplyTo(results).Cast<UserEntity>().AsQueryable().

EDIT: With following code too:

var results = this.repository.GetAll()
return options.ApplyTo(results).Cast<UserEntity>().AsQueryable().
alexzheludov commented 4 years ago

@mikepizzo any updates on this?

Bitz commented 3 years ago

I really do appreciate the teams stance of "maybe if we ignore it, it will go away".

The astounding thing about this is that this is an incredibly simplistic use case, and yet OData does not support it.

Still an issue!

burakkaradag commented 2 years ago

I am having the same problem. I can't select, expand. Anyone found a solution to this problem?

    public async Task<ActionResult<IQueryable<ProductODataDTO>>> Get(ODataQueryOptions<ProductODataDTO> oDataQueryOpts)
    {
        var products = _genericReadRepository.GetAll<Product>().ProjectToType<ProductODataDTO>();
        var odataQueryFilter = oDataQueryOpts.ApplyTo(products.AsQueryable()).Cast<ProductODataDTO>();
        return Ok(odataQueryFilter.ToList());
    }

netcoreapp3.1 OData 7.5.7

We progressed most of the project with Odata. It would be very disappointing to take it back. Help please!

mikepizzo commented 2 years ago

@xuzhg -- can you take a look at this? note latest repro is w/.net 6 and aspnetcore odata 8.0.10.

mikepizzo commented 2 years ago

@burakkaradag -- I was just noting that you said you had this issue with .net6.0 and OData 8.0.10 (so it's a current issue)

burakkaradag commented 2 years ago

is this the repro? https://github.com/OData/AspNetCoreOData

burakkaradag commented 2 years ago

ow no sory @mikepizzo

netcoreapp3.1 OData 7.5.7

I have the problem in this version I updated my first comment

burakkaradag commented 2 years ago

hurray!

this method worked for me

before

        [HttpGet]
        [EnableQuery]
        [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(List<SalesOrdersODataDTO>))]
        public async Task<ActionResult<IQueryable<ProductODataDTO>>> Get(ODataQueryOptions<ProductODataDTO> oDataQueryOpts)
    {
        var products = _genericReadRepository.GetAll<Product>().ProjectToType<ProductODataDTO>();
        var odataQueryFilter = oDataQueryOpts.ApplyTo(products.AsQueryable()).Cast<ProductODataDTO>();
        return Ok(odataQueryFilter.ToList());
    }

after

        [HttpGet]
        [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(List<SalesOrdersODataDTO>))]
        public async Task<ActionResult<List<SalesOrdersODataDTO>>> Get(ODataQueryOptions<SalesOrdersODataDTO> oDataQueryOpts)
        {
            var salesOrders = _genericReadRepository.GetAll<SalesOrder>().ProjectToType<SalesOrdersODataDTO>();
            var odataQueryFilter = oDataQueryOpts.ApplyTo(salesOrders.AsQueryable()) as IQueryable<dynamic>;
            return Ok(odataQueryFilter.ToList());
        }

EnableQuery attribute removed and used IQueryable<dynamic>

@mikepizzo Thank you also for your quick turnaround.