我正在尝试过滤EntityFramework中显式加载的结果。
当我不应用任何过滤器时,显式加载有效但在应用过滤器后它不会加载结果。
类
public partial class Student
{
public int StudentId { get; set; }
public int CourseId { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public virtual ICollection<Grade> Grades { get; set; }
}
public partial class Grade
{
public int GradeId { get; set; }
public string Value { get; set; }
public string Status { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
Fluent API Mapping
modelBuilder.Entity<Grade>()
.HasMany(e => e.Students)
.WithMany(x => x.Grades)
.Map(m => m.ToTable("StudentGrades").MapLeftKey("GradeId").MapRightKey("StudentId"));
用法
这可以工作并填充student.Grades
属性。
using (var context = new Model1())
{
context.Configuration.LazyLoadingEnabled = false;
var student = context.Students.Single(x => x.StudentId == 1);
context.Entry(student).Collection(x => x.Grades).Load();
}
生成的SQL如下所示:
SELECT
[Extent2].[GradeId] AS [GradeId],
[Extent2].[Value] AS [Value],
[Extent2].[Status] AS [Status]
FROM [dbo].[StudentGrades] AS [Extent1]
INNER JOIN [dbo].[Grades] AS [Extent2] ON [Extent1].[GradeId] = [Extent2].[GradeId]
WHERE [Extent1].[StudentId] = 1 // this is parameterized in the actual hit.
当我运行此查询时,我会得到完整的结果。
但是,当我应用过滤并使用以下行时,它不会填充student.Grades
。
context.Entry(student).Collection(x => x.Grades).Query().Where(x => x.Status == "A").Load();
此行生成此查询:
SELECT
[Extent2].[GradeId] AS [GradeId],
[Extent2].[Value] AS [Value],
[Extent2].[Status] AS [Status]
FROM [dbo].[StudentGrades] AS [Extent1]
INNER JOIN [dbo].[Grades] AS [Extent2] ON [Extent1].[GradeId] = [Extent2].[GradeId]
WHERE ([Extent1].[StudentId] = 1) AND ('A' = [Extent2].[Status])
//the "1" is parameterized in the actual hit.
当我针对数据库手动运行时,我在SQL Server中获得了正确筛选的结果。问题是这不会在C#对象中填充student.Grades
。
答案 0 :(得分:5)
MSDN Article - 在显式加载相关实体时应用过滤器部分中提到了此技术,因此它应该受支持并正常工作。奇怪的是,它适用于one-to-many
关系,many-to-many
具有显式链接表和2个one-to-many
关联,但不适用于具有隐式链接表的many-to-many
。
我没有解释为什么(没找到相关文档)。我也没有解释为什么,但将它与急切加载其他集合的请求相结合可以解决问题:
context.Entry(student).Collection(s => s.Grades)
.Query().Where(g => g.Status == "A")
.Include(g => g.Students)
.Load();
这个缺点(如评论中所提到的)是它还会加载很多属于加载成绩的学生。
所以更好的方法是使用显式链接表和这样的关系:
型号:
public partial class Student
{
public int StudentId { get; set; }
public int CourseId { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public virtual ICollection<StudentGrade> StudentGrades { get; set; }
}
public partial class Grade
{
public int GradeId { get; set; }
public string Value { get; set; }
public string Status { get; set; }
public virtual ICollection<StudentGrade> StudentGrades { get; set; }
}
public class StudentGrade
{
public int StudentId { get; set; }
public int GradeId { get; set; }
public virtual Student Student { get; set; }
public virtual Grade Grade { get; set; }
}
配置:
modelBuilder.Entity<StudentGrade>()
.ToTable("StudentGrades")
.HasKey(e => new { e.GradeId, e.StudentId });
modelBuilder.Entity<StudentGrade>()
.HasRequired(e => e.Grade)
.WithMany(x => x.StudentGrades)
.HasForeignKey(e => e.GradeId)
.WillCascadeOnDelete();
modelBuilder.Entity<StudentGrade>()
.HasRequired(e => e.Student)
.WithMany(x => x.StudentGrades)
.HasForeignKey(e => e.StudentId)
.WillCascadeOnDelete();
现在显式加载不需要技巧,并且会加载已填充的StudentGrade
个已过滤的GradeId
个实体,从而避免加载其他StudentId
和Grade
个对象:
Student
答案 1 :(得分:1)
这是预期的
第一个Load()是DbCollectionEntry.Load()
第二个是IQueryable.Load()
基本上你在一个空的IQueryable上调用Load
尝试
var grades = context.Students.Where(s => s.StudentId == 1).SelectMany(s => s.Grades).Where(g => g.Status == "A").ToList();
SELECT
[Extent2].[GradeId] AS [GradeId],
[Extent2].[Value] AS [Value],
[Extent2].[Status] AS [Status]
FROM [dbo].[StudentGrades] AS [Extent1]
INNER JOIN [dbo].[Grades] AS [Extent2] ON [Extent1].[GradeId] = [Extent2].[GradeId]
WHERE (N'A' = [Extent2].[Status]) AND (1 = [Extent1].[StudentId])