使用NHibernate标准的复杂查询

时间:2014-08-20 13:46:53

标签: nhibernate criteria

我在使用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>();

1 个答案:

答案 0 :(得分:1)

我会使用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的员工并使用该实例。