-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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 Filters caching predicates causes issues #10363
Comments
If I had to guess... The first time the function body is entered of the query filter, you see if it can be reduced and store this value. Unfortunately this results in the predicate becoming just Upon further testing, if we flip the bool, Even if we set |
Uncomment using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
#pragma warning disable 169
namespace SoftDeleteTest
{
public class Program
{
private static void Main()
{
//SetupDatabase();
Console.WriteLine("=== Should see deleted === ");
using (var db = new BloggingContext("diego", true))
{
var blogs = db.Blogs
.AsNoTracking()
.Include(b => b.Posts)
.ToList();
foreach (var blog in blogs)
{
Console.WriteLine(
$"{blog.Url.PadRight(33)} [Tenant: {db.Entry(blog).Property("TenantId").CurrentValue}]");
foreach (var post in blog.Posts)
{
Console.WriteLine($" - {post.Title.PadRight(30)} [IsDeleted: {post.IsDeleted}]");
}
Console.WriteLine();
Console.WriteLine();
}
}
Console.WriteLine("=== Should NOT see deleted === ");
using (var db = new BloggingContext("diego", false))
{
var blogs = db.Blogs
.AsNoTracking()
.Include(b => b.Posts)
.ToList();
foreach (var blog in blogs)
{
Console.WriteLine(
$"{blog.Url.PadRight(33)} [Tenant: {db.Entry(blog).Property("TenantId").CurrentValue}]");
foreach (var post in blog.Posts)
{
Console.WriteLine($" - {post.Title.PadRight(30)} [IsDeleted: {post.IsDeleted}]");
}
Console.WriteLine();
}
}
Console.ReadLine();
}
private static void SetupDatabase()
{
using (var db = new BloggingContext("diego", true))
{
if (db.Database.EnsureCreated())
{
db.Blogs.Add(
new Blog
{
Url = "http://sample.com/blogs/fish",
Posts = new List<Post>
{
new Post { Title = "Fish care 101" },
new Post { Title = "Caring for tropical fish" },
new Post { Title = "Types of ornamental fish" }
}
});
db.Blogs.Add(
new Blog
{
Url = "http://sample.com/blogs/cats",
Posts = new List<Post>
{
new Post { Title = "Cat care 101" },
new Post { Title = "Caring for tropical cats" },
new Post { Title = "Types of ornamental cats" }
}
});
db.SaveChanges();
using (var andrewDb = new BloggingContext("andrew", true))
{
andrewDb.Blogs.Add(
new Blog
{
Url = "http://sample.com/blogs/catfish",
Posts = new List<Post>
{
new Post { Title = "Catfish care 101" },
new Post { Title = "History of the catfish name" }
}
});
andrewDb.SaveChanges();
}
db.Posts
.Where(
p => p.Title == "Caring for tropical fish"
|| p.Title == "Cat care 101")
.ToList()
.ForEach(p => db.Posts.Remove(p));
db.SaveChanges();
}
}
}
}
public class BloggingContext : DbContext
{
public BloggingContext(string tenant, bool viewDeleted)
{
Tenant = tenant;
ViewDeleted = viewDeleted;
}
public string Tenant { get; set; }
public bool ViewDeleted { get; set; } = false;
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=Demo.QueryFilters;Trusted_Connection=True;ConnectRetryCount=0;");
}
#region Configuration
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().Property<string>("TenantId").HasField("_tenantId");
// Configure entity filters
modelBuilder.Entity<Blog>().HasQueryFilter(b => EF.Property<string>(b, "TenantId") == Tenant);
modelBuilder.Entity<Post>().HasQueryFilter(p => ViewDeleted || !p.IsDeleted);
}
#endregion
public override int SaveChanges()
{
ChangeTracker.DetectChanges();
foreach (var item in ChangeTracker.Entries().Where(
e =>
e.State == EntityState.Added && e.Metadata.GetProperties().Any(p => p.Name == "TenantId")))
{
item.CurrentValues["TenantId"] = Tenant;
}
foreach (var item in ChangeTracker.Entries<Post>().Where(e => e.State == EntityState.Deleted))
{
item.State = EntityState.Modified;
item.CurrentValues["IsDeleted"] = true;
}
return base.SaveChanges();
}
}
#region Entities
public class Blog
{
private string _tenantId;
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public bool IsDeleted { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
#endregion
} |
Duplicate of #9825 |
@ajcvickers nice, thanks! Will remember to check closed issues next time 😉 |
Using your example code.
Changing the predicate to something more complex
and it works fine.
The text was updated successfully, but these errors were encountered: