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.

EF Core 2.0 Possible regression when filtering results

See original GitHub issue

Code that works fine in EF Core1.1.5 fails using EF Core 2.0.2 (same in 2.1 preview). We are working on a 40+ projects solution with a lot of repositories.

Migrating from EF Core 1.1.x to 2.0.x we have found that a lot of our queries now are failing with a NullReferenceException, or missing data on our endpoints, due to Navigation properties not materialized when using a filter (Where clause) that cannot be translated in SQL, so executed locally.

Not sure if it is a bug, but I didn’t find anything in the release notes related to this behavior change.

I add a SLN example with the exact same code running EF Core 1 or 2 EF1vsEF2.zip

The example is using the SQLite provider running an InMemory DB, but we use Azure SQL Databases in our project…with the same outcome.

Here is the Program.cs file (all needs self contained):

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;

namespace EF1
{
    static class Program
    {
        class Entity
        {
            [Key]
            public int EntityID { get; set; }

            public int StatusID { get; set; }

            [ForeignKey("StatusID")]
            [InverseProperty("Entities")]
            public virtual Status Status { get; set; }
        }

        class Status
        {
            [Key]
            public int StatusID { get; set; }

            [Required]
            public string Code { get; set; }


            [InverseProperty("Status")]
            public ICollection<Entity> Entities { get; set; }

            public Status()
            {
                Entities = new HashSet<Entity>();
            }
        }

        class AppDbContext : DbContext
        {
            public DbSet<Entity> Entities { get; set; }
            public DbSet<Status> Statuses { get; set; }
            public AppDbContext(DbContextOptions options) : base(options)
            {
            }
        }

        static async Task Main(string[] args)
        {
            var connection = new SqliteConnection("DataSource=:memory:");
            connection.Open();

            var options = new DbContextOptionsBuilder<AppDbContext>()
                .UseSqlite(connection)
                .Options;

            try
            {
                //Seed DB
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("EF CORE 1.1.5 Test");

                using (var ctx = new AppDbContext(options))
                {
                    ctx.Database.EnsureCreated();

                    var status = new Status { Code = "CODE VALUE" };
                    var entity = new Entity { Status = status };

                    ctx.Add(entity);
                    ctx.SaveChanges();
                }

                //Fetch data
                using (var ctx = new AppDbContext(options))
                {
                    var entities = await ctx.Entities
                        .Include(e => e.Status)
                        .Where(e => e.FakeFilterNotRenderedInSQL())
                        .ToListAsync();

                    foreach (var entity in entities)
                    {
                        Console.ForegroundColor = entity.Status == null ? ConsoleColor.Red : ConsoleColor.Green;
                        Console.WriteLine($"After Materialization -> Entity has ID: {entity.EntityID}, Status code: {entity.Status?.Code ?? "Not Materialized"}");
                        Console.ForegroundColor = ConsoleColor.White;
                    }
                }
            }
            finally
            {
                connection.Close();
            }

            Console.ReadKey();
            
        }

        static bool FakeFilterNotRenderedInSQL(this Entity entity)
        {
            Console.ForegroundColor = entity.Status == null ? ConsoleColor.Red : ConsoleColor.Green;
            Console.WriteLine($"Inside IQueryable<Entity>.Where clause -> Entity has ID: {entity.EntityID}, Status code: {entity.Status?.Code ?? "Not Materialized"}");
            Console.ForegroundColor = ConsoleColor.White;
            return true;
        }
    }
}

Here is the outputs:

image

Our migration is in standby because identifying all code that could be impacted by that regression will need a significat amount of time and tests.

A workaround is to materialize the entities before filtering, but it is not a satisfying solution regarding all code needed to be modified.

Best regards.

Issue Analytics

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

github_iconTop GitHub Comments

1reaction
maumarcommented, Mar 23, 2018

Correlated collection + include unification could help this scenario - problem currently is that we generate query plan for include before we know that client evaluation is needed. Unification will push that to VisitQueryModel phase, at which point we already know that predicate needs client evaluation and can come up with a different execution plan.

0reactions
divegacommented, Mar 23, 2018

@maumar please add the reference to this scenario in the Include unification issue. Then you can close as duplicate.

Read more comments on GitHub >

github_iconTop Results From Across the Web

EF Core 2.0.0 Query Filter is Caching TenantId (Updated ...
It works only if the dynamic part is provided by direct property of the target DbContext derived class (or one of its base...
Read more >
Filtering Results Using Filtered Include Method in EF Core
In this article, we are going to show you how to use Filtered Include method in EF Core to filter results inside the...
Read more >
EF Core releases and planning
Current EF Core releases and schedule/planning details for future releases. ... NET Standard 2.0, Expired December 13, 2022, Announcement.
Read more >
What is new in EF Core 2.0
EF Core 2.0 includes a new feature we call Model-level query filters. This feature allows LINQ query predicates (a boolean expression typically ...
Read more >
Entity Framework Core 3.0: A Foundation for the Future
Because EF Core can't translate Reverse into SQL, it first executes what it can to get the results and then performs the Reverse...
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