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