在NHibernate中加载没有重复的多级集合

时间:2011-09-30 18:41:33

标签: nhibernate nhibernate-criteria

我的问题与此问题非常相似(尚未真正回答):Nhibernate: distinct results in second level Collection

我有这个对象模型:

   class EntityA
   {
        ...
        IList<EntityB> BList { get; protected set; }
        ...
   }

   class EntityB
   {
       ... does NOT reference its parent EntityA...
       IList<EntityC> CList { get; protected set; }
   }

他们是一对多的关系。 EntityB和C do not 具有对其父对象的对象引用。

我想通过执行以下三个SQL查询来完全加载集合,以避免笛卡尔连接:

 SELECT id, ... FROM EntityA;
 SELECT id, idA, ... FROM EntityB;
 SELECT id, idB, ... FROM EntityC;

这样,DAL就拥有了正确填充对象的所有信息。但是由于EntityB不知道它的父亲是谁, 是nHibernate谁负责正确填充集合。

可以吗?


可以使用笛卡尔积来执行此解决方法,但它需要修改我的模型以提供集合设置器,并且在我的脑海中有资格作为DAL技术问题的补丁。

     ICriteria criteria = session.CreateCriteria<EntityA>()
                         .SetFetchMode("BList", FetchMode.Join)
                         .SetFetchMode("BList.CList", FetchMode.Join)
                         .SetResultTransformer(new DistinctRootEntityResultTransformer());

     IList<EntityA> listA = criteria.List<EntityA>();

     foreach (EntityA objA in listA) {
        objA.BList = objA.BList.Distinct().ToList();
        foreach (EntityB objB in objB.BList) {
           objB.CList = objB.CList.Distinct().ToList();
        }
     }

1 个答案:

答案 0 :(得分:5)

您是否尝试过这种语法:

var entities = session.QueryOver<EntityA>().Where(...).List();
var entityIds = entities.Select(e => e.Id).ToArray();
session.QueryOver<EntityA>()
    .WhereRestrictionOn(a => a.Id)
    .IsIn(entityIds)
    .Fetch(e => e.BList).Eager
    .List();

var bEntityIds = entities
    .SelectMany(e => e.BList)
    .Select(b => b.Id)
    .ToArray();

session.QueryOver<EntityB>()
    .WhereRestrictionOn(b => b.Id)
    .IsIn(bEntityIds).Fetch(e => e.CList).Eager
    .List();

这应该触发你提到的三个选择。它可能看起来有点混乱,但它正在利用会话的第一级缓存,这可确保第一个集合中的所有实体在执行时都使用已加载的集合进行更新。

此外,对于第二次和第三次查询,您不需要支付任何复杂查询逻辑的惩罚。数据库应该使用第二个查询的主索引(甚至可能根据您的设置进行群集)和连接的外键来进行极低成本的查询。