我有一个简单的测试对象模型,其中有学校,学校有一群学生。
我想找回一所学校及其所有超过一定年龄的学生。
我执行以下查询,该查询获得特定学校和超过特定年龄的孩子:
public School GetSchoolAndStudentsWithDOBAbove(int schoolid, DateTime dob)
{
var school = this.Session.CreateCriteria(typeof(School))
.CreateAlias("Students", "students")
.Add(Expression.And(Expression.Eq("SchoolId", schoolid), Expression.Gt("students.DOB", dob)))
.UniqueResult<School>();
return school;
}
这一切都运行正常,我可以看到查询进入数据库并返回预期的行数。
但是,当我执行以下任一操作时,它会通过运行另一个查询向我提供给定学校的学生总数(无论前面的请求):
foreach (Student st in s.Students)
{
Console.WriteLine(st.FirstName);
}
Assert.AreEqual(s.Students.Count, 3);
任何人都可以解释原因吗?
答案 0 :(得分:2)
您在School类上进行了查询,并且限制了结果,而不是映射的相关对象。
现在有很多方法可以做到这一点。 你可以像IanL所说的那样制作一个静态过滤器,但它不是很灵活。 您可以像mxmissile一样迭代集合,但这很丑陋(特别是考虑到延迟加载注意事项)
我会提供两种不同的解决方案: 在第一个维护您的查询时,您在集合上触发动态过滤器(维护一个延迟加载的集合)并对数据库进行往返:
var school = GetSchoolAndStudentsWithDOBAbove(5, dob);
IQuery qDob = nhSession.CreateFilter(school.Students, "where DOB > :dob").SetDateTime("dob", dob);
IList<Student> dobedSchoolStudents = qDob.List<Student>();
在第二个解决方案中,只需一次性取得学校和学生:
object result = nhSession.CreateQuery(
"select ss, st from School ss, Student st
where ss.Id = st.School.Id and ss.Id = :schId and st.DOB > :dob")
.SetInt32("schId", 5).SetDateTime("dob", dob).List();
ss是一个School对象,st是一个学生集合。
这绝对可以使用您现在使用的标准查询(使用预测)来完成
答案 1 :(得分:1)
不幸的是s.Students
不会包含您的“查询”结果。您必须为学生创建单独的查询才能实现目标。
foreach(var st in s.Students.Where(x => x.DOB > dob))
Console.WriteLine(st.FirstName);
警告:根据您的映射情况,它仍将继续第二次访问数据库,它仍将检索所有学生。
我不确定,但您可以使用Projections在一个查询中完成所有这些操作,但我绝不是专家。
答案 2 :(得分:0)
您可以选择过滤数据。如果它有一个查询的单个实例mxmissle选项将是更好的选择。
Nhibernate Filter Documentation
过滤器确实有用,但根据您使用的版本,可能存在过滤集合未正确缓存的问题。