Including entity with a query filter can cause not loading the root entity
See original GitHub issueI would expect the following code returns the instance of Child (the same instances). But it depends on a query filter. If child’s master is filtered, the second line returns null.
Child child = dbContext.Set<Child>().Where(c => c.Id == 1).FirstOrDefault();
Child childInludingMaster = dbContext.Set<Child>().Include(m => m.Master /* has a query filter */).Where(c => c.Id == 1).FirstOrDefault();
Steps to reproduce
- Install NuGet packages
- Create database IssueDemo (or change connection string)
- Generate database migration
- Run unit test
[TestClass]
public class TestIssue
{
[TestMethod]
public void DbSet_ShouldReturnSameInstancesWheneverIncludeIsCalled()
{
// Arrange
#region Data initialization
int childId;
using (var dbContext = new MyDbContext())
{
dbContext.Database.Migrate();
Child child = new Child { IsDeleted = false };
Master master = new Master { IsDeleted = true, Children = new List<Child> { child } };
dbContext.Set<Master>().AddRange(master);
dbContext.SaveChanges();
childId = child.Id;
}
#endregion
// Act
using (var dbContext = new MyDbContext())
{
Child child = dbContext.Set<Child>().Where(c => c.Id == childId).FirstOrDefault();
Child childInludingMaster = dbContext.Set<Child>().Include(m => m.Master /* has a query filter */).Where(c => c.Id == childId).FirstOrDefault();
// Assert
Assert.AreSame(Object.ReferenceEquals(child, childInludingMaster), "Instances should be the same.");
}
}
}
#region Model
public class Child
{
public int Id { get; set; }
public Master Master { get; set; }
public int MasterId { get; set; }
public bool IsDeleted { get; set; }
}
public class Master
{
public int Id { get; set; }
public List<Child> Children { get; set; }
public bool IsDeleted { get; set; }
}
#endregion
#region MyDbContext
public class MyDbContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Master>().HasQueryFilter(item => !item.IsDeleted);
modelBuilder.Entity<Child>().HasQueryFilter(item => !item.IsDeleted);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=IssueDemo;Trusted_Connection=True;");
}
}
#endregion
Further technical details
EF Core version: 2.0.2 Database Provider: Microsoft.EntityFrameworkCore.SqlServer Target .NET Framework: 4.6.1
Issue Analytics
- State:
- Created 5 years ago
- Comments:19 (12 by maintainers)
Top Results From Across the Web
Including entity with a QueryFilter results in root entity not ...
Our desired behavior for these entities is, that once they are archived, they are not displayed to the user anymore, when queried as...
Read more >EntityFramework-Plus Query Filter - Child/Relational entity ...
Filtering using instance context + Lazy Loading is a limitation for this library. There is currently no workaround that I may recommend you....
Read more >Global Query Filters - EF Core
Global query filters are LINQ query predicates applied to Entity Types in the metadata model (usually in OnModelCreating ).
Read more >Eager Loading of Related Data - EF Core
In case of tracking queries, results of Filtered Include may be unexpected due to navigation fixup. All relevant entities that have been queried ......
Read more >Using Query Builder
This is currently available only for filtering ( where ) and sorting ( orderBy ), only the root entity will be selected. To...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Thank you very much for the answer.
My description contains a synthetic scenario, that’s why Child/Master classes are not well understandable. I will describe it in more details. I am preparing guidelines how to use Entity Framework Core in our applications (tens of line of business applications, tailor-made software development). The issue is related to soft deletes. Softdelete-aware object in for us an object which is no more usable. This for us means:
Retrieving list of (softdelete-aware) objects When retrieving list of objects, a deleted object must not be returned. We want to be able to suppress this constraint to enable undelete scenario (like IgnoreQueryFilters). Example: If a user see list of cars, no deleted car is in the list.
Handling collections of (softdelete-aware) objects When an object A has a collection of objects B, we USUALLY do not want to load deleted objects B in the A’s collection. We would like to have an option to decide (in model definition) whether the collections should or should not contain deleted object. Example: Let’s say, we have a Group and it has a collection of Members in the group (group.Members). When we load a group, we do not want deleted members to be in the collection group.Members.
Handling references to (softdelete-aware) objects When an object A has a reference to object B, we want never to filter out this object. Example: Let’s say Car has a (required) Color. We want to have a car with color even when the color is deleted. Example: An Invoice was issued by an Accountant and we would like to know who it was even when the Accountant is deleted.
There is a conflict in our goal (we never filter out references, but we USUALLY filter out collections), but it fits our practice and business requirements. That’s our interpretation of soft delete 😉.
I tried the behavior of the Entity Framework Core and I did not expect the Include method should limit the root objects. Furthermore, I did not notice such information in the documentation (https://docs.microsoft.com/en-us/ef/core/querying/related-data). XML documentation of Include extension method (https://github.com/aspnet/EntityFrameworkCore/blob/a588722e10f34a12b9cea3d655fd26aafd91051b/src/EFCore/EntityFrameworkQueryableExtensions.cs) is “Specifies related entities to include in the query results.“ (in brief). So I did not expect…
After going through all the linked issues here
Looking at this issue all the related issues,
Overall, global query filters are filters which will be applied to DbSet whenever querying for certain entity type. Further, referential integrity constraint should match model, with & without filter because model metadata is used to generate appropriate join. None of above scenarios are actual use case for global query filters.
Folks above who are facing issue or anyone new who arrives here with similar issue, consider using a different solution than global query filters. If the data cannot conform to model configuration about a relationship with & without query filter then it is not supported scenario with query filter. Consider using different refactoring, including manual joins for your case.
Marking this as closed by design.