我在使用NHibernate标准执行以下任务时遇到问题。
我有一个对象TNA,具有以下属性,以及其他属性。
Id (int)
OrgUnit(一个具有Name字符串属性的对象)
Employee(一个具有下面列出的属性的对象)
TrainingRecords的列表(每个列表包含一个Course对象、一个RequiredBy日期属性和一个Status字符串属性)
一个TNATemplate对象(包含一个限定字符串属性)
客户对象
Employee对象包含以下属性:
在姓前面(字符串)姓(字符串)
职业(字典对象)
Shift(字典)
字典对象包含一个id (int)和一个描述(string)
用户可以在我们的系统中创建他们自己的过滤器,这些过滤器是使用标准实现的。
我需要查询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行。然而,报告必须获取组织单元、员工、课程和客户的数据。
下面是我的代码-谁能看到一个方法,使这更有效?:
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>();
我会使用NHibernate Futures来解决这个问题
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的雇员并使用那个id。