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

Posted

技术标签:

【中文标题】在 NHibernate 中加载没有重复的多级集合【英文标题】:Loading multi-level collections without duplicates in NHibernate 【发布时间】:2011-09-30 18:41:33 【问题描述】:

我的问题与这个问题非常相似(没有真正回答):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 确实具有对其父对象的对象引用。

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

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

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

可以吗??


可以使用笛卡尔积来解决这个问题,但它需要修改我的模型以提供集合设置器,并且在我看来,这可以作为 DAL 技术问题的补丁。 p>

     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();
        
     

【问题讨论】:

那不是笛卡尔积。这只是很多复制的数据。由于 ResultTransformer,对于每个额外的 EntityC、EntityA 来说,EntityB 的副本都会很好。您可以通过将该集合设为ISet&lt;&gt; 来避免那些受骗的EntityB。尽管如此,仍然有很多额外的数据通过网络传输。 确实,您说它不是笛卡尔积是对的,因为它不是 N 对 N,对此感到抱歉。 ISet 是无序的,因此不幸的是,它不是我的方案的真正选择。 【参考方案1】:

你试过这个语法吗:

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();

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

此外,您不必为第二个和第三个查询所使用的任何复杂查询逻辑付出代价。数据库应该使用主索引进行第二个查询(甚至可能根据您的设置进行集群)和连接的外键以实现极低成本的查询。

【讨论】:

谢谢地精!!我是 NHibernate 的新手,所以我永远不会自己想出这个语法!使用分析器,我现在只有 3 个查询。他们使用 WHERE IN 子句,但总是在主键和外键索引上,所以应该很快。您代码的最后一行有一个小错误,这就是最终的工作:session.QueryOver&lt;EntityB&gt;().WhereRestrictionOn(b =&gt; b.Id).IsIn(bEntityIds).Fetch(e =&gt; e.CList).Eager.List(); 没问题 :-) 是的 - 这是两种不同解决方案的糟糕组合...我会更新 :-) 如果你真的对 NHibernate 的复杂方式感兴趣,我强烈鼓励你查看ayende.com 的博客存档。一定要先看最新的帖子,因为他在之前的帖子中有一些过时的信息。

以上是关于在 NHibernate 中加载没有重复的多级集合的主要内容,如果未能解决你的问题,请参考以下文章

在 Nhibernate 中加载与获取

从 nhibernate 中的复合 ID 列表中加载对象

在 R 中加载 xlsx 库时出错 [重复]

JS 有时无法在 Chrome 和其他浏览器中加载 [重复]

URLImage 从不在 SwiftUI 的小部件应用程序中加载图像 [重复]

快速在视图控制器中加载集合视图控制器