using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.EntityFrameworkCore;
namespace TestEF
{
public class Product
{
[Key]
public Guid Id { get; set; }
public List<Tag> Tags { get; set; }
}
public class Tag
{
[Key]
public Guid Id { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Tag> Tags { get; set; }
public MyContext() { }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=:memory:");
}
}
public class Program
{
public static void Main(string[] args)
{
using (var db = new MyContext())
{
db.Database.OpenConnection();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
var expectedTagsIds = new List<Guid> { Guid.NewGuid(), Guid.NewGuid() };
var result = db.Products
.Where(product => expectedTagsIds
.All(expectedTagId => product.Tags.Select(tag => tag.Id)
.Any(tagId => tagId == expectedTagId)))
.ToListAsync()
.Result;
}
}
}
}
The above code results in an exception. It only occurs if you do ToListAsync(), it doesn't occur if you do ToList().
Unhandled Exception: System.AggregateException: One or more errors occurred. (Expression of type 'System.Boolean' cannot be used for parameter of type 'System.Threading.Tasks.Task`1[System.Boolean]' of method 'Boolean Result[Boolean](System.Threading.Tasks.Task`1[System.Boolean])') ---> System.ArgumentException: Expression of type 'System.Boolean' cannot be used for parameter of type 'System.Threading.Tasks.Task`1[System.Boolean]' of method 'Boolean Result[Boolean](System.Threading.Tasks.Task`1[System.Boolean])'
at System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arguments, ParameterInfo pi)
at System.Linq.Expressions.Expression.Call(MethodInfo method, Expression arg0)
at System.Linq.Expressions.MethodCallExpression1.Rewrite(Expression instance, IList`1 args)
at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.TaskBlockingExpressionVisitor.Visit(Expression expression)
at System.Linq.Expressions.ExpressionVisitor.VisitLambda[T](Expression`1 node)
at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.TaskBlockingExpressionVisitor.Visit(Expression expression)
at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes)
at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.TaskBlockingExpressionVisitor.Visit(Expression expression)
at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.VisitQueryModel(QueryModel queryModel)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor.VisitQueryModel(QueryModel queryModel)
at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.RelationalEntityQueryableExpressionVisitor.VisitSubQuery(SubQueryExpression expression)
at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.ExpressionVisitorBase.Visit(Expression node)
at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.ReplaceClauseReferences(Expression expression, IQuerySource querySource, Boolean inProjection)
at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.VisitWhereClause(WhereClause whereClause, QueryModel queryModel, Int32 index)
at Remotion.Linq.QueryModelVisitorBase.VisitBodyClauses(ObservableCollection`1 bodyClauses, QueryModel queryModel)
at Remotion.Linq.QueryModelVisitorBase.VisitQueryModel(QueryModel queryModel)
at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.VisitQueryModel(QueryModel queryModel)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor.VisitQueryModel(QueryModel queryModel)
at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.CreateAsyncQueryExecutor[TResult](QueryModel queryModel)
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddAsyncQuery[TResult](Object cacheKey, Func`1 compiler)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.System.Collections.Generic.IAsyncEnumerable<TResult>.GetEnumerator()
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.<ToListAsync>d__129`1.MoveNext()
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at TestEF.Program.Main(String[] args)
EF Core version: 1.0.0-rc2-final
Operating system: Windows 10
Visual Studio version: Visual Studio 2015
Other details about my project setup:
EFCore.Sqlite 1.0.0-rc2-final
Same problem occurs with AsAsyncEnemurable as well.
@Zoltu
The issue is with TaskBlockingExpressionVisitor, it never checks if rewrite was done before on the Task<T> resulting expression.
Here is a workaround (possibly fix?)
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal
{
public class TaskBlockingExpressionVisitor : ExpressionVisitorBase, ITaskBlockingExpressionVisitor
{
private bool _insideBlockingCall;
public override Expression Visit(Expression expression)
{
if (expression != null && !_insideBlockingCall)
{
var typeInfo = expression.Type.GetTypeInfo();
if (typeInfo.IsGenericType
&& (typeInfo.GetGenericTypeDefinition() == typeof(Task<>)))
{
return Expression.Call(
_resultMethodInfo.MakeGenericMethod(typeInfo.GenericTypeArguments[0]),
expression);
}
}
return base.Visit(expression);
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.DeclaringType == typeof(TaskBlockingExpressionVisitor))
{
_insideBlockingCall = true;
return base.VisitMethodCall(node);
}
_insideBlockingCall = false;
return base.VisitMethodCall(node);
}
private static readonly MethodInfo _resultMethodInfo
= typeof(TaskBlockingExpressionVisitor).GetTypeInfo()
.GetDeclaredMethod(nameof(Result));
[UsedImplicitly]
private static T Result<T>(Task<T> task) => task.GetAwaiter().GetResult();
}
}
cc @anpete