Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query builder with EF core can't return fsharp tuples (and sometimes new objects) using async methods #3782

Open
Lanayx opened this issue Oct 20, 2017 · 8 comments
Labels
Area-Queries Query expressions and library implementation Bug Impact-Medium (Internal MS Team use only) Describes an issue with moderate impact on existing code.
Milestone

Comments

@Lanayx
Copy link
Contributor

Lanayx commented Oct 20, 2017

I'm getting an exсeption when using this code

    [<CLIMutable>]
    type Product =
        {
            Id: string
            Name: string
        }

    let db = new MyContext()
    let q = query {
        for p in db.Products do
        select (p.Id, p.Name)
    }
    let result = q.ToListAsync().Result

If I choose select p or select p.Id insead of select (p.Id, p.Name) , this works fine. If I write ToList instead of ToListAsync this works fine as well. C# version with Tuples works well. With provided F# code it fails with this exception:

Unhandled Exception: System.InvalidOperationException: The source IQueryable doesn't implement IAsyncEnumerable<System.Tuple`2[System.String,System
.String]>. Only sources that implement IAsyncEnumerable can be used for Entity Framework asynchronous operations.
   at Microsoft.EntityFrameworkCore.Extensions.Internal.QueryableExtensions.AsAsyncEnumerable[TSource](IQueryable`1 source)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToke
n)

Also noticed that select { Id = p.Id; Name = p.Name } works, while select { Name = p.Name; Id = p.Id } doesn't and fails with exception:

Unhandled Exception: System.AggregateException: One or more errors occurred. (When called from 'VisitLambda', rewriting a node of type 'System.Linq
.Expressions.ParameterExpression' must return a non-null value of the same type. Alternatively, override 'VisitLambda' and change it to not visit c
hildren of this type.) ---> System.InvalidOperationException: When called from 'VisitLambda', rewriting a node of type 'System.Linq.Expressions.Par
ameterExpression' must return a non-null value of the same type. Alternatively, override 'VisitLambda' and change it to not visit children of this
type.
   at System.Linq.Expressions.ExpressionVisitor.VisitAndConvert[T](ReadOnlyCollection`1 nodes, String callerName)
   at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.ExpressionVisitorBase.VisitLambda[T](Expression`1 node)
   at System.Linq.Expressions.Expression`1.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.RelationalProjectionExpressionVisitor.Visit(Expression expression)
   at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
   at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.RelationalProjectionExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallE
xpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.RelationalProjectionExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.VisitSelectClause(SelectClause selectClause, QueryModel queryModel)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor.VisitSelectClause(SelectClause selectClause, QueryModel queryModel)
   at Remotion.Linq.Clauses.SelectClause.Accept(IQueryModelVisitor visitor, 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.Storage.Database.CompileAsyncQuery[TResult](QueryModel queryModel)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileAsyncQueryCore[TResult](Expression query, INodeTypeProvider nodeTypeProvide
r, IDatabase database)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass24_0`1.<CompileAsyncQuery>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddAsyncQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileAsyncQuery[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.System.Collections.Generic.IAsyncEnumerable<TResult>.GetEnumerator()
   at System.Linq.AsyncEnumerable.<Aggregate_>d__6`3.MoveNext()
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
  • Windows 10
  • .NET Core 2.0.0
  • Microsoft.EntityFrameworkCore 2.0
  • FSharp.Core 4.2.3
  • FSharp.NET.Sdk 1.0.5
@ForNeVeR
Copy link
Contributor

ForNeVeR commented Oct 20, 2017

Notes

  1. This works:

    let db = new MyContext()
    let q1 = query {
        for p in db.Products do
        select p.Id
    }
    let result = q1.ToListAsync().Result

    I.e. only the code with tuples (like select (p.Id, p.Name)) won't work.

  2. It seems it doesn't work because q1 (from my sample) has Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[System.String] type (EF-friendly) while q (from @Lanayx sample) has System.Linq.EnumerableQuery`1[System.Tuple`2[System.String,System.String]] type (non EF-efiendly?).

  3. This also works:

    let db = new MyContext()
    let q1 = query {
        for p in db.Products do
        select (Tuple<string, string>(p.Id, p.Name))
    }
    let result = q1.ToListAsync().Result

@Lanayx Lanayx changed the title Query builder with EF core can't return new objects using async methods Query builder with EF core can't return fsharp tuples (and sometimes new objects) using async methods Oct 21, 2017
@dsyme
Copy link
Contributor

dsyme commented Oct 24, 2017

There is another lingering bug in the treatment of tuples in query { ... } expressions e.g. #47, I assume this may be related

I assume using an intermediate record type will work.

@dsyme dsyme added Bug Impact-Medium (Internal MS Team use only) Describes an issue with moderate impact on existing code. Area-Library Issues for FSharp.Core not covered elsewhere labels Oct 24, 2017
@Lanayx
Copy link
Contributor Author

Lanayx commented Oct 25, 2017

There are two bugs here

  1. The tuple doesn't work
  2. The order of intermediate record fields matters, which shouldn't

@ForNeVeR
Copy link
Contributor

@Lanayx could you please provide an example when the order matters? It'll be useful.

@Lanayx
Copy link
Contributor Author

Lanayx commented Oct 25, 2017

The example is already in the main post right above

@cartermp cartermp added this to the 16.0 milestone Aug 29, 2018
@cartermp cartermp modified the milestones: 16.0, 16.1 Feb 21, 2019
@cartermp cartermp modified the milestones: 16.1, 16.2 Apr 23, 2019
@cartermp cartermp modified the milestones: 16.2, Backlog Apr 30, 2019
@dsyme dsyme added Area-Queries Query expressions and library implementation and removed Area-Library Issues for FSharp.Core not covered elsewhere labels Mar 30, 2022
@vzarytovskii vzarytovskii moved this to Not Planned in F# Compiler and Tooling Jun 17, 2022
@lucasteles
Copy link

The problem happens with anonymous records too,

task {
    let db = app.Services.GetRequiredService<AppDbContext>()

    let query1 = // EntityQueryable<<>f__AnonymousType1003022151<string, string>>
        db.Blogs.Select(fun x -> {| Path = x.Slug; Title = x.Name |})

    let query2 = // System.Linq.Enumerable+SelectEnumerableIterator`2[Microsoft.FSharp.Linq.RuntimeHelpers.AnonymousObject`2[System.String,System.String],<>f__AnonymousType1003022151`2[System.String,System.String]]
        query {
            for x in db.Blogs do
                select {| Path = x.Slug; Title = x.Name |}
        }

    let! result1 = query1.ToArrayAsync() // works
    let! result2 = query2.ToArrayAsync() // throws

    return printfn $"%A{result1} | %A{result2}"
}

@xperiandri
Copy link
Contributor

Any progress on this?

@lucasteles
Copy link

There is also #15648 which impacts the usage of query builder on EF

@github-project-automation github-project-automation bot moved this from Done to In Progress in F# Compiler and Tooling Jan 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Queries Query expressions and library implementation Bug Impact-Medium (Internal MS Team use only) Describes an issue with moderate impact on existing code.
Projects
Archived in project
Development

No branches or pull requests

8 participants