显式加载N:M with Filtering

时间:2016-07-21 20:59:56

标签: c# entity-framework

我正在尝试过滤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

2 个答案:

答案 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个实体,从而避免加载其他StudentIdGrade个对象:

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])