question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

LINQ Average conversion to SQL is correct in ver2-preview1, but incorrect in version 2

See original GitHub issue

In EF Core 2.0.0-preview1-final release the Linq Average command was correctly converted to the SQL AVG command. In release 2.0.0 this seems to be broken.

If you are seeing an exception, include the full exceptions details (message and stack trace).

System.InvalidOperationException : Sequence contains no elements.
   at Microsoft.EntityFrameworkCore.Query.QueryMethodProvider.GetResult[TResult](IEnumerable`1 valueBuffers, Boolean throwOnNullResult)
   at lambda_method(Closure , QueryContext , ValueBuffer )
   at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.ProjectionShaper.TypedProjectionShaper`3.Shape(QueryContext queryContext, ValueBuffer valueBuffer)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.BufferlessMoveNext(Boolean buffer)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.MoveNext()
   at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext()
   at System.Collections.Generic.List`1.AddEnumerable(IEnumerable`1 enumerable)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at Ver2.UnitTests.TestAverageVer2.TakeAverageOfTwoBooks() in c:\users\jonsm\Source\Repos\EfTestDifferentVersions\Ver2\UnitTests\TestAverageVer2.cs:line 73

Steps to reproduce

I have a Book entity class with a collection of Review entity classes that contain NumVotes. I want to calculate the average number of votes. A Book can have zero to many Reviews.

My entity classes are:

public class Book                                   
{
    public int BookId { get; set; } 
    public string Title { get; set; }
    public string Description { get; set; }
    public DateTime PublishedOn { get; set; }
    public string Publisher { get; set; }
    public decimal Price { get; set; }
    public string ImageUrl { get; set; }

    //----------------------------------------------
    //relationships

    public PriceOffer Promotion { get; set; }       
    public ICollection<Review> Reviews { get; set; }
    public ICollection<BookAuthor> AuthorsLink { get; set; }                   
}
public class Review                      
{
    public int ReviewId { get; set; }
    public string VoterName { get; set; }
    public int NumStars { get; set; }
    public string Comment { get; set; }

    //-----------------------------------
    //Relationships

    public int BookId { get; set; }      
}

My test code is below, and it fails on the database query when running EF Core 2.0.0, but works correctly if running EF Core 2.0.0-preview1-final. Note: the method TwoBooksOneWithReviews creates two Book entities; one with no reviews and the second with two reviews.

private class Dto
{
    public string Title { get; set; }
    public double? AveVotes { get; set; }
}

[Fact]
public void TakeAverageOfTwoBooks()
{
    //SETUP
    var options = SqliteInMemory.CreateOptions<EfCoreContext>();

    using (var context = new EfCoreContext(options))
    {
        context.Database.EnsureCreated();

        context.AddRange(SeedData.TwoBooksOneWithReviews());
        context.SaveChanges();
        var logs = context.SetupLogging();

        //VERIFY
        var result = context.Books.Select(x => new Dto
        {
            Title = x.Title,
            AveVotes = x.Reviews.Select(y => y.NumStars).Average()
        }).ToList();

        //VERIFY
        result.First().AveVotes.ShouldBeNull();
        result.Last().AveVotes.ShouldEqual(4);
        foreach (var log in logs)
        {
            _output.WriteLine(log);
        }
    }
}

If I run exactly the same test on release 2.0.0-preview1-final then it works correctly and outputs the following SQL

Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT "x"."Title", (
    SELECT AVG(CAST("y"."NumStars" AS REAL))
    FROM "Review" AS "y"
    WHERE "x"."BookId" = "y"."BookId"
) AS "AveVotes"
FROM "Books" AS "x"

Further technical details

EF Core version: 2.0.0 incorrect (2.0.0-preview1-final correct) Database Provider: Microsoft.EntityFrameworkCore.Sqlite (but fails on SqlServer too) Operating system: Windows 10 IDE: VS2017 15.3

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Reactions:1
  • Comments:13 (9 by maintainers)

github_iconTop GitHub Comments

3reactions
JonPSmithcommented, Aug 30, 2017

Could I ask if this bug is fixed in the planned patch update, 2.0.1? My reasons for asking this are:

  1. It seems that you know what the problem is - DetermineAggregateThrowingBehavior. I therefore assume its not hard to fix.
  2. The SQL code produced is very poor, and you get a lot of warnings in the log.
  3. I am trying to release the book Entity Framework in Action soon and I include Average as an example in the book - see demo web site http://efcoreinaction.com/ (currently running EF Core 1.1.0).
2reactions
ajcvickerscommented, Aug 30, 2017

@anpete to follow up with a fix and we can discuss whether to patch or not.

Read more comments on GitHub >

github_iconTop Results From Across the Web

LINQ to SQL query has wrong values in results
I have a LINQ query that has the incorrect results, but when I profile the SQL generated, the SQL results are correct.
Read more >
Why is Select().Contains() ok while Any() is not?
StartsWith may be converted to like in sql query, but it depends on the level of support from the database provider. b. Any...
Read more >
8 Tips and Tricks for Writing the Best Queries in LINQ to ...
We all know that the LINQ-to-Entities query will be converted to a SQL query and will be executed against a database.
Read more >
Language Integrated Query (LINQ) and Lambda Expressions
4.0 Introduction. Language Integrated Query (LINQ) is a great way to access data from many different sources. LINQ provides a single querying model...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found