dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.82k stars 3.2k forks source link

Find with QueryFilter does not find an item that has been filtered out #13401

Closed JonPSmith closed 2 years ago

JonPSmith commented 6 years ago

I am having trouble with QueryFilter interacting with Find. I expected the Find method to find an entity even if it was filtered out by the query filter, but it doesn't. It is also inconsistent, in that if I read in the item to find as a tracked entity first, then it finds it.

This seems so basic that I think it must be my problem, but I can't find anything I have done that explains that. Please could you look at it.

Steps to reproduce

Entity class

public class Author
{
    public Author() { }

    public int AuthorId { get;  set; }
    public string Name { get;  set; }
    public string Email { get; set; }

    public bool SoftDelete { get; set; }

    public ICollection<BookAuthor> BooksLink { get; set; }
}

My DbContext

public class EfCoreContext : DbContext
{
    public EfCoreContext(                             
        DbContextOptions<EfCoreContext> options)      
        : base(options) {}

    public DbSet<Book> Books { get; set; }
    public DbSet<Author> Authors { get; set; }
    public DbSet<Order> Orders { get; set; }

    protected override void
        OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfiguration(new BookConfig());       
        modelBuilder.ApplyConfiguration(new BookAuthorConfig());
        modelBuilder.ApplyConfiguration(new OrderConfig());
        modelBuilder.ApplyConfiguration(new LineItemConfig());

        modelBuilder.Entity<Author>().HasQueryFilter(x => !x.SoftDelete);
    }
}

My unit test that fails on the test for ShouldNotBeNull

[Fact]
public void TestFindWithQueryFilterOk()
{
    //SETUP
    var options = SqliteInMemory.CreateOptions<EfCoreContext>();
    using (var context = new EfCoreContext(options))
    {
        context.Database.EnsureCreated();
        var author = new Author {Name = "test", SoftDelete = true};
        context.Add(author);
        context.SaveChanges();
    }
    using (var context = new EfCoreContext(options))
    {
        context.Authors.Count().ShouldEqual(0);
        context.Authors.IgnoreQueryFilters().Count().ShouldEqual(1);

        //ATTEMPT
        var found = context.Find<Author>(1);

        //VERIFY
        found.ShouldNotBeNull();
    }
}

Another test where I load the entity before calling Find. This unit test passes

[Fact]
public void TestFindWithQueryFilterAndLoadingOk()
{
    //SETUP
    var options = SqliteInMemory.CreateOptions<EfCoreContext>();
    using (var context = new EfCoreContext(options))
    {
        context.Database.EnsureCreated();
        var author = new Author { Name = "test", SoftDelete = true };
        context.Add(author);
        context.SaveChanges();
    }
    using (var context = new EfCoreContext(options))
    {
        context.Authors.Count().ShouldEqual(0);
        var author = context.Authors.IgnoreQueryFilters().Single();

        //ATTEMPT
        var found = context.Find<Author>(1);

        //VERIFY
        found.ShouldNotBeNull();
    }
}

PS. The unit tests use Sqlite, but it also goes wrong the the actual application which uses SQL Server.

Further technical details

EF Core version: netcoreapp2.1 - (dotnet --version says 2.1.401) Database Provider: (e.g. Microsoft.EntityFrameworkCore.Sqlite 2.1.3) Operating system: IDE: (e.g. Visual Studio 2017 15.4)

ajcvickers commented 6 years ago

@JonPSmith Query filters impact what comes from the database--i.e. queries--but does not impact how entities that are already tracked are handled. If you have a tracked entity--for example, after running a query with no filter--then you can work with it in the normal way. This is useful when you're dealing with some code that, for example, needs to look at entities that have been soft-deleted.

JonPSmith commented 6 years ago

Thanks. I wrongly assumed that Find did NOT use the QueryFilter. Thank you for pointing out my mistake.