WebApi icon indicating copy to clipboard operation
WebApi copied to clipboard

Lambda Error: No coercion operator is defined between types

Open NetTecture opened this issue 6 years ago • 10 comments

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`1Api.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 avatar Jun 04 '18 16:06 NetTecture

And 5 months later: Assigned, not worked on.

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

NetTecture avatar Nov 27 '18 17:11 NetTecture

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().

TheAifam5 avatar Jul 10 '19 14:07 TheAifam5

@mikepizzo any updates on this?

alexzheludov avatar May 06 '20 15:05 alexzheludov

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!

Bitz avatar Aug 13 '21 01:08 Bitz

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!

burakkaradag avatar Jul 26 '22 19:07 burakkaradag

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

mikepizzo avatar Jul 26 '22 19:07 mikepizzo

@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)

mikepizzo avatar Jul 26 '22 19:07 mikepizzo

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

burakkaradag avatar Jul 26 '22 19:07 burakkaradag

ow no sory @mikepizzo

netcoreapp3.1 OData 7.5.7

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

burakkaradag avatar Jul 26 '22 19:07 burakkaradag

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.

burakkaradag avatar Jul 26 '22 22:07 burakkaradag