【问题标题】:Entity Framework: Fetch multiple collections with filters applied实体框架:使用过滤器获取多个集合
【发布时间】:2013-12-11 16:15:35
【问题描述】:

给出下一个模型:

public class User{
    public int IdUser { get; set;}
    public virtual ICollection<Project> Projects { get; set;}
    public virtual ICollection<Exam> Exams { get; set;}
}

public class Project{
    public int IdProject { get; set;}
    public bool Current { get; set;}
    public virtual User { get; set;}
}

public class Exam{
    public int IdExam { get; set;}
    public int score { get; set;}
    public virtual User { get; set;}
}

我需要从一个 current=trueexams 获得 score 大于 4给定用户。

当我需要过滤导航属性时,为了避免将所有记录带入内存并将过滤器应用到内存中,我会执行以下操作:

IQueryable<Project> = context.Entry(user).Collection(x => x.Projects).Query().Where(n => n.Current).ToList();

这样,我只从数据库中获取当前项目。避免将所有项目检索到内存然后在内存上应用过滤器的其他方式。

所以现在,我也想做同样的事情(只带重要的记录),但我不知道当我有多个集合时该怎么做。

你能帮帮我吗?谢谢!

【问题讨论】:

标签: .net entity-framework ef-code-first


【解决方案1】:

您的存储库将很快被在导航属性上返回具有不同类型过滤器的实体的方法填满。也许你应该有一个看起来像这样的方法:

   public GetUser(int userid, 
                  Expression<System.Func<Project, System.Boolean>> projectFilter,
                  Expression<System.Func<Exam, System.Boolean>> examFilter)
    {
       var user = context.Users.Find(userid); 

       context.Entry(user)
              .Collection(c => c.Projects)
              .Query()
              .Where(projectFilter)
              .Load(); 

       context.Entry(user)
              .Collection(c => c.Exams)
              .Query()
              .Where(examFilter)
              .Load();

       return user
    }

你这样称呼它:

var userincludingCurrentProjectsAndExamsWithScoresLessThan4 = 
                         userRepo.GetUser(id, p => p.Current, e => e.Score > 4)

不要忘记从集合中删除 virtual 关键字,否则延迟加载会在应用过滤器之前将整个集合拉出数据库:

public class User
{
    public int IdUser { get; set;}
    public ICollection<Project> Projects { get; set;}
    public ICollection<Exam> Exams { get; set;}
}

编辑 我不得不说,这种部分填充导航属性的技术对我来说有点好笑。我认为大多数人所做的是将数据投影到 DTO 或 ViewModel 中以实现您想要做的事情。然后 DTO 可以具有更有意义的属性名称。像这样的:

var query = from f in context.Foos.Include(x => x.Bars).Include(y => y.Bazs)
            select new FooDTO
            {
                ID = f.ID,
                Toms = f.Bars.Where(b => b.Name == "Tom").ToList(),
                Dicks = f.Bazs.Where(b => b.Name == "Dick").ToList()
            };

string sql = query.ToString();//useful in debugger. Remove once satisfied.

但如果你真的想投射到实体而不是 DTO 中,你可以这样做:

var usersWithTomsAndDicksOohErr = 
(from f in context.Foos.Include(x => x.Bars).Include(y => y.Bazs)
    select new //anonymous. You can't construct entities using LINQ to Entities
    {
        ID = f.ID,
        Toms = f.Bars.Where(b => b.Name == "Tom").ToList(),
        Dicks = f.Bazs.Where(b => b.Name == "Dick").ToList()
    })
    .AsEnumerable()//moves only the data you want into memory
    .Select(x => new Foo//now you can construct the entity using Linq to Objects
    {
       ID = x.ID,
       Bars = x.Toms,
       Bazs = x.Dicks
    });

【讨论】:

  • 是的,但这样一来,它会运行一个查询来检索项目,并运行另一个查询来检索考试,不是吗?
  • 确实如此。我认为您需要重新考虑部分填充导航属性的技术。查看我的编辑
  • 你是最棒的@Colin!你已经解决了问题!现在我只需一个查询就可以做到!非常感谢你的朋友!
  • 我已经删除了“包含”,它也可以工作。有什么区别吗?我的意思是,我已经完成了 context.Foos,而不是 context.Foos.Include(x => x.Bars).Include(y => y.Bazs)。但是在选择中我得到了集合,似乎没有区别。我错了吗?
  • @ArielScherman 您是否删除了 virtual 关键字以防止延迟加载?不同之处在于sql。致电query.ToString() 进行检查
【解决方案2】:

怎么样:

var q = context.Set<Project>().Where(x => x.Current && x.User.Exams.Where(y => y.score > 4).Count() > 0)

或者可能是

var q = context.Set<Project>().Where(x => x.Current && x.User.Exams.Any(y => y.score > 4))

【讨论】:

    猜你喜欢
    • 2016-02-15
    • 2016-12-27
    • 1970-01-01
    • 2014-11-28
    • 2011-10-28
    • 2011-06-18
    • 2019-04-14
    • 1970-01-01
    • 2017-11-08
    相关资源
    最近更新 更多