Efcore: ArgumentException when Cast IQueryable to interface

Created on 27 Sep 2019  路  4Comments  路  Source: dotnet/efcore

Cast on IQueryable throws ArgumentException on EFCore 3.0 but it works on EFCore 2.2.6

static void Main(string[] args)
        {
            var id = GetEntityCollection().First().Id;
            var simpleResult = GetQueryable(typeof(MockEntity)).Cast<IDomainEntity>().FirstOrDefault(x => x.Id == id);
        }

        static IQueryable GetQueryable(Type entityType)
        {
            var dbContext = new MockContext();
            dbContext.AddRange(GetEntityCollection());
            dbContext.SaveChanges();
            var setMethod = typeof(DbContext).GetMethod("Set").MakeGenericMethod(entityType);
            return (IQueryable)setMethod.Invoke(dbContext, Array.Empty<object>());
        }

I added a repro project here: https://github.com/sandrohanea/EFCore30IssueCast

Exception Message:

Expression of type 'System.Linq.IQueryable`1[EFCoreIssueCast.IDomainEntity]' cannot be used for parameter of type 'System.Linq.IQueryable`1[EFCoreIssueCast.MockEntity]' of method 'System.Linq.IQueryable`1[EFCoreIssueCast.MockEntity] Where[MockEntity](System.Linq.IQueryable`1[EFCoreIssueCast.MockEntity], System.Linq.Expressions.Expression`1[System.Func`2[EFCoreIssueCast.MockEntity,System.Boolean]])' (Parameter 'arg0')

StackTrace:

   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(MethodInfo method, Expression arg0, Expression arg1)
   at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.ProcessFirstSingleLastOrDefault(NavigationExpansionExpression source, MethodInfo genericMethod, LambdaExpression predicate, Type returnType)
   at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.ExpandAndReduce(Expression query, Boolean applyInclude)
   at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.Expand(Expression query)
   at Microsoft.EntityFrameworkCore.Query.QueryTranslationPreprocessor.Process(Expression query)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass9_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
   at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source, Expression`1 predicate)
   at EFCoreIssueCast.Program.Main(String[] args) in C:\Users\Sandro\Source\Repos\EFCoreIssueCast\EFCoreIssueCast\Program.cs:line 42

EF Core version: 3.0.0
Database provider: Any, the repro project is with Microsoft.EntityFrameworkCore.InMemoryDatabase
Target framework: .NET Core 3.0
Operating system: Windows 10.0.17763 Build 17763
IDE: Visual Studio 2019 16.3

closed-fixed customer-reported type-bug

Most helpful comment

Decision:
Casting to implemented interface is removed from the tree - so translates correctly.
Casting to non-implemented interface is kept in the tree - and throws translation error
Casting to object is removed from the tree - so translates correctly.
Casting to basetype/derivedtype will update the navigation tree accordingly.

All 4 comments

I'm running into the same issue currently in LightQuery.

I've been able to work around it, by changing .Cast<object>() to using dynamic.

// Old - worked before 3.0
var totalCount = await queryable.Cast<object>().CountAsync();

// New - working in 3.0
dynamic dynamicQueryable = queryable;
var totalCount = await EntityFrameworkQueryableExtensions.CountAsync(dynamicQueryable);

Poaching

@roji Co-assigning @smitpatel to look at this today for 3.1.

Decision:
Casting to implemented interface is removed from the tree - so translates correctly.
Casting to non-implemented interface is kept in the tree - and throws translation error
Casting to object is removed from the tree - so translates correctly.
Casting to basetype/derivedtype will update the navigation tree accordingly.

Was this page helpful?
0 / 5 - 0 ratings