我在使用NHibernate标准执行以下任务时遇到问题。
我有一个具有以下属性的对象TNA以及其他属性。
Id(int)
OrgUnit(具有Name字符串属性的对象)
员工(具有下列属性的对象)
TrainingRecords列表(每个都包含一个Course对象,一个RequiredBy日期属性和一个Status字符串属性)
TNATemplate对象(包含Qualification字符串属性)
客户对象
Employee对象包含以下属性:
Forename(string)
姓(字符串)
职业(字典对象)
Shift(字典)
字典对象包含id(int)和description(字符串)
用户可以在我们的系统中创建自己的过滤器,这些过滤器使用标准实现。
我需要查询TNA对象以生成相当复杂的报告,但我们的数据结构使这很困难。
目前,我们有多个选择N + 1问题,我发现难以解决。
我们当前的标准(没有任何过滤)产生以下sql语句:
SELECT this_1_.OrgUnit_Id as y0_,
this_.Employee_id as y1_,
trainingre1_.Course_id as y2_,
trainingre1_.RequiredBy as y3_,
this_.TNATemplate_id as y4_,
trainingre1_.TNAStatus as y5_,
this_1_.Customer_id as y6_
FROM tblTNA this_
inner join tblModule this_1_
on this_.Module_id = this_1_.Id
left outer join tblTraining trainingre1_
on this_.Module_id = trainingre1_.TNA_Id
left outer join tblModule trainingre1_1_
on trainingre1_.Module_id = trainingre1_1_.Id
WHERE this_1_.Customer_id = 9 /* @p0 */
and this_1_.IsArchive = 0 /* @p1 */
and this_1_.IsActive = 1 /* @p2 */
and not (trainingre1_.TNAStatus = 3 /* @p3 */)
这会产生35,000行。但是,报告必须为OrgUnit,员工,课程和客户获取数据。
以下是我的代码 - 任何人都可以看到一种提高效率的方法吗?:
DetachedCriteria dc = this.BuildPermissions(moduleUser, typeof(TNA));
ICriteria criteria = dc.GetExecutableCriteria(this.Session);
criteria.SetReadOnly(true);
criteria.SetFlushMode(FlushMode.Never);
criteria.SetFetchMode("OrgUnit", FetchMode.Join);
criteria.SetFetchMode("Employee", FetchMode.Join);
criteria.SetFetchMode("TrainingRecords", FetchMode.Join);
criteria.SetFetchMode("TrainingRecords.Course", FetchMode.Join);
criteria.SetFetchMode("TNATemplate", FetchMode.Join);
criteria.CreateAlias("TrainingRecords", "TrainingRecords", NHibernate.SqlCommand.JoinType.LeftOuterJoin);
criteria.Add(Restrictions.Not(Restrictions.Eq("TrainingRecords.TNAStatus", TNAStatus.Optional)));
ProjectionList projectionList =
Projections.ProjectionList()
.Add(Projections.Property("OrgUnit"), "OrgUnit")
.Add(Projections.Property("Employee"), "Employee")
.Add(Projections.Property("TrainingRecords.Course"), "Course")
.Add(Projections.Property("TrainingRecords.RequiredBy"), "RequiredBy")
.Add(Projections.Property("TNATemplate"), "TNATemplate")
.Add(Projections.Property("TrainingRecords.TNAStatus"), "TNAStatus")
.Add(Projections.Property("Customer"), "Customer");
ICriteria result = criteria.SetProjection(projectionList)
.SetResultTransformer(Transformers.AliasToBean<TrainingMatrix>());
return result.List<TrainingMatrix>();
更新
我添加了以下代码,但仍然多次查询Course表 - 似乎从未调用子查询:
criteria.Future<TrainingMatrix>();
var myCourseSubQuery = QueryOver.Of<Course>()
.Where(a => a.Customer.Id == moduleUser.Customer.Id)
.Select(x => x.Id, x => x.CourseName, x => x.CourseDate);
IEnumerable<Course> courses = this.Session.QueryOver<Course>()
.WithSubquery.WhereProperty(c => c.Id)
.In(myCourseSubQuery)
.Future();
ICriteria result = criteria.SetProjection(projectionList)
.SetResultTransformer(Transformers.AliasToBean<TrainingMatrix>());
return result.List<TrainingMatrix>();
答案 0 :(得分:1)
NHibernate需要检索主查询中包含的所有OrgUnits,Employees,Courses和Customers。
因此,对于这些项中的每一项,请创建将来的查询。
(我使用了不可编译的伪代码和QueryOver语法混合,但它应该得到重点)
IEnumerable<Customer> customers = s.QueryOver<Customer>()
.WithSubquery.WhereProperty(c => c.Id)
.In(myCustomerSubQuery)
.Future();
IEnumerable<Course> couse = s.QueryOver<Course>()
.WithSubquery.WhereProperty(c => c.Id)
.In(myCourseSubQuery)
.Future();
等
然后将最终的TrainingMatrix
查询作为未来的查询。
因为它们是Future
个查询,所以当您解决其中任何一个查询时,它们都会批量运行。您将在一次往返数据库中有效地获得所需的一切。
当nHibernate需要实例化一个员工实例时,它会看到它已经检索到该id的员工并使用该实例。